diff --git a/.github/workflows/build-plugin-zip.yml b/.github/workflows/build-plugin-zip.yml index d1621ed5106aab..149faee274206e 100644 --- a/.github/workflows/build-plugin-zip.yml +++ b/.github/workflows/build-plugin-zip.yml @@ -69,7 +69,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 with: token: ${{ secrets.GUTENBERG_TOKEN }} show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} @@ -165,7 +165,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 with: ref: ${{ needs.bump-version.outputs.release_branch || github.ref }} show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} @@ -222,7 +222,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 with: fetch-depth: 2 ref: ${{ needs.bump-version.outputs.release_branch }} @@ -311,14 +311,14 @@ jobs: if: ${{ endsWith( needs.bump-version.outputs.new_version, '-rc.1' ) }} steps: - name: Checkout (for CLI) - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 with: path: main ref: trunk show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} - name: Checkout (for publishing) - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 with: path: publish # Later, we switch this branch in the script that publishes packages. diff --git a/.github/workflows/bundle-size.yml b/.github/workflows/bundle-size.yml index 8eafe4267bc433..1065421044373b 100644 --- a/.github/workflows/bundle-size.yml +++ b/.github/workflows/bundle-size.yml @@ -37,7 +37,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 with: fetch-depth: 1 show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} diff --git a/.github/workflows/check-components-changelog.yml b/.github/workflows/check-components-changelog.yml index 77fdf4759f7de3..d995d641fae57d 100644 --- a/.github/workflows/check-components-changelog.yml +++ b/.github/workflows/check-components-changelog.yml @@ -20,7 +20,7 @@ jobs: - name: 'Get PR commit count' run: echo "PR_COMMIT_COUNT=$(( ${{ github.event.pull_request.commits }} + 1 ))" >> "${GITHUB_ENV}" - name: Checkout code - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 with: ref: ${{ github.event.pull_request.head.ref }} repository: ${{ github.event.pull_request.head.repo.full_name }} diff --git a/.github/workflows/create-block.yml b/.github/workflows/create-block.yml index 2b935469264800..7c26cb6e14e760 100644 --- a/.github/workflows/create-block.yml +++ b/.github/workflows/create-block.yml @@ -24,7 +24,7 @@ jobs: os: ['macos-latest', 'ubuntu-latest', 'windows-latest'] steps: - - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} diff --git a/.github/workflows/end2end-test.yml b/.github/workflows/end2end-test.yml index c919e733360ffe..16680038e0db64 100644 --- a/.github/workflows/end2end-test.yml +++ b/.github/workflows/end2end-test.yml @@ -27,7 +27,7 @@ jobs: totalParts: [8] steps: - - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} @@ -53,35 +53,61 @@ jobs: - name: Archive debug artifacts (screenshots, traces) uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 - if: always() + if: ${{ !cancelled() }} with: - name: failures-artifacts + name: failures-artifacts--${{ matrix.part }} path: artifacts/test-results if-no-files-found: ignore - name: Archive flaky tests report uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 - if: always() + if: ${{ !cancelled() }} with: - name: flaky-tests-report + name: flaky-tests-report--${{ matrix.part }} path: flaky-tests if-no-files-found: ignore + merge-artifacts: + name: Merge Artifacts + if: ${{ !cancelled() }} + needs: [e2e-playwright] + runs-on: ubuntu-latest + outputs: + has-flaky-test-report: ${{ !!steps.merge-flaky-tests-reports.outputs.artifact-id }} + steps: + - name: Merge failures artifacts + uses: actions/upload-artifact/merge@v4 + # Don't fail the job if there aren't any artifacts to merge. + continue-on-error: true + with: + name: failures-artifacts + # Retain the merged artifacts in case of a rerun. + pattern: failures-artifacts* + delete-merged: true + + - name: Merge flaky tests reports + id: merge-flaky-tests-reports + uses: actions/upload-artifact/merge@v4 + continue-on-error: true + with: + name: flaky-tests-report + pattern: flaky-tests-report* + delete-merged: true + report-to-issues: name: Report to GitHub - needs: [e2e-playwright] - if: ${{ always() }} + needs: [merge-artifacts] + if: ${{ needs.merge-artifacts.outputs.has-flaky-test-report == 'true' }} runs-on: ubuntu-latest steps: # Checkout defaults to using the branch which triggered the event, which # isn't necessarily `trunk` (e.g. in the case of a merge). - - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 with: ref: trunk show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} - uses: actions/download-artifact@v4.1.7 - id: download_artifact # Don't fail the job if there isn't any flaky tests report. continue-on-error: true with: @@ -89,16 +115,13 @@ jobs: path: flaky-tests - name: Setup Node.js and install dependencies - if: ${{ steps.download_artifact.outcome == 'success' }} uses: ./.github/setup-node - name: Npm build - if: ${{ steps.download_artifact.outcome == 'success' }} # TODO: We don't have to build the entire project, just the action itself. run: npm run build:packages - name: Report flaky tests - if: ${{ steps.download_artifact.outcome == 'success' }} uses: ./packages/report-flaky-tests with: repo-token: '${{ secrets.GITHUB_TOKEN }}' diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml index bcd0fee6453fdb..633f62d5ed28c9 100644 --- a/.github/workflows/gradle-wrapper-validation.yml +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -6,7 +6,7 @@ jobs: name: 'Validation' runs-on: ubuntu-latest steps: - - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} - uses: gradle/wrapper-validation-action@v3 diff --git a/.github/workflows/performance.yml b/.github/workflows/performance.yml index 4afa2dc5dcddeb..7f239652774df1 100644 --- a/.github/workflows/performance.yml +++ b/.github/workflows/performance.yml @@ -32,7 +32,7 @@ jobs: WP_ARTIFACTS_PATH: ${{ github.workspace }}/artifacts steps: - - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} diff --git a/.github/workflows/php-changes-detection.yml b/.github/workflows/php-changes-detection.yml index ba34e0d806185e..3b813c8f8d48fb 100644 --- a/.github/workflows/php-changes-detection.yml +++ b/.github/workflows/php-changes-detection.yml @@ -10,7 +10,7 @@ jobs: if: ${{ github.repository == 'WordPress/gutenberg' || github.event_name == 'pull_request' }} steps: - name: Check out code - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 with: fetch-depth: 0 show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} diff --git a/.github/workflows/publish-npm-packages.yml b/.github/workflows/publish-npm-packages.yml index 11dfdb878ef289..94397afd7b4bca 100644 --- a/.github/workflows/publish-npm-packages.yml +++ b/.github/workflows/publish-npm-packages.yml @@ -31,7 +31,7 @@ jobs: steps: - name: Checkout (for CLI) if: ${{ github.event.inputs.release_type != 'wp' }} - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 with: path: cli ref: trunk @@ -39,7 +39,7 @@ jobs: - name: Checkout (for publishing) if: ${{ github.event.inputs.release_type != 'wp' }} - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 with: path: publish # Later, we switch this branch in the script that publishes packages. @@ -49,7 +49,7 @@ jobs: - name: Checkout (for publishing WP major version) if: ${{ github.event.inputs.release_type == 'wp' && github.event.inputs.wp_version }} - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 with: path: publish ref: wp/${{ github.event.inputs.wp_version }} diff --git a/.github/workflows/pull-request-automation.yml b/.github/workflows/pull-request-automation.yml index 05d28f888d0ae0..099203bbffe720 100644 --- a/.github/workflows/pull-request-automation.yml +++ b/.github/workflows/pull-request-automation.yml @@ -12,7 +12,7 @@ jobs: steps: # Checkout defaults to using the branch which triggered the event, which # isn't necessarily `trunk` (e.g. in the case of a merge). - - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 with: ref: trunk show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} diff --git a/.github/workflows/rnmobile-android-runner.yml b/.github/workflows/rnmobile-android-runner.yml index 5d1d476226b12f..a4dce407d1c0ff 100644 --- a/.github/workflows/rnmobile-android-runner.yml +++ b/.github/workflows/rnmobile-android-runner.yml @@ -23,7 +23,7 @@ jobs: steps: - name: checkout - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} diff --git a/.github/workflows/rnmobile-ios-runner.yml b/.github/workflows/rnmobile-ios-runner.yml index 5056527d097bd9..516f783c11e401 100644 --- a/.github/workflows/rnmobile-ios-runner.yml +++ b/.github/workflows/rnmobile-ios-runner.yml @@ -23,7 +23,7 @@ jobs: native-test-name: [gutenberg-editor-rendering] steps: - - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} diff --git a/.github/workflows/static-checks.yml b/.github/workflows/static-checks.yml index 12c8931efca066..ff3fe96d505f6f 100644 --- a/.github/workflows/static-checks.yml +++ b/.github/workflows/static-checks.yml @@ -22,7 +22,7 @@ jobs: if: ${{ github.repository == 'WordPress/gutenberg' || github.event_name == 'pull_request' }} steps: - - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} diff --git a/.github/workflows/storybook-pages.yml b/.github/workflows/storybook-pages.yml index 56b7471f06d9b3..7486ea32533e6a 100644 --- a/.github/workflows/storybook-pages.yml +++ b/.github/workflows/storybook-pages.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 with: ref: trunk show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 22bca2dc78186b..a4a639e183d5bf 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -32,7 +32,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} @@ -70,7 +70,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} @@ -121,7 +121,7 @@ jobs: name: Build JavaScript assets for PHP unit tests runs-on: ubuntu-latest steps: - - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} @@ -170,7 +170,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} @@ -281,7 +281,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} @@ -351,7 +351,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} diff --git a/.github/workflows/upload-release-to-plugin-repo.yml b/.github/workflows/upload-release-to-plugin-repo.yml index 8a92d0443d577a..8f57a749b0601d 100644 --- a/.github/workflows/upload-release-to-plugin-repo.yml +++ b/.github/workflows/upload-release-to-plugin-repo.yml @@ -96,7 +96,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 with: ref: ${{ matrix.branch }} token: ${{ secrets.GUTENBERG_TOKEN }} diff --git a/README.md b/README.md index 360b2851be092b..9c920337ef5941 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Get hands on: check out the [block editor live demo](https://wordpress.org/guten Extending and customizing is at the heart of the WordPress platform, this is no different for the Gutenberg project. The editor and future products can be extended by third-party developers using plugins. -Review the [Create a Block tutorial](/docs/getting-started/devenv/get-started-with-create-block.md) for the fastest way to get started extending the block editor. See the [Developer Documentation](https://developer.wordpress.org/block-editor/#develop-for-the-block-editor) for extensive tutorials, documentation, and API references. +Review the [Quick Start Guide](https://developer.wordpress.org/block-editor/getting-started/quick-start-guide/) for the fastest way to get started extending the block editor. See the [Block Editor Handbook](https://developer.wordpress.org/block-editor/) for extensive tutorials, documentation, and API references. Also, check the [WordPress Developer Blog](https://developer.wordpress.org/blog/) for great articles about block development, among other topics. ### Contribute to Gutenberg diff --git a/bin/test-create-block.sh b/bin/test-create-block.sh index 7959334a8e30ec..99b7e8e6082604 100755 --- a/bin/test-create-block.sh +++ b/bin/test-create-block.sh @@ -69,7 +69,7 @@ status "Building block..." ../node_modules/.bin/wp-scripts build status "Verifying build..." -expected=7 +expected=9 actual=$( find build -maxdepth 1 -type f | wc -l ) if [ "$expected" -ne "$actual" ]; then error "Expected $expected files in the \`build\` directory, but found $actual." diff --git a/changelog.txt b/changelog.txt index aee9a5c1a15753..0bb052d82feb0b 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,7 +1,6 @@ == Changelog == -= 18.3.0-rc.1 = - += 18.3.0 = ## Changelog @@ -233,6 +232,8 @@ The following contributors merged PRs in this release: @aaronrobertshaw @afercia @ajlende @carolinan @cbravobernal @colorful-tones @DaniGuardiola @desrosj @draganescu @ellatrix @fabiankaegy @fullofcaffeine @geriux @huubl @itzmekhokan @jameskoster @jasmussen @jeryj @jorgefilipecosta @jsnajdr @juanfra @juanmaguitar @lanresmith @MaggieCabrera @Mamaduka @mirka @ntsekouras @oandregal @ockham @ramonjd @retrofox @richtabor @SantosGuillamot @scruffian @shail-mehta @sirreal @stokesman @sunil25393 @swissspidy @t-hamano @talldan @twstokes @tyxla @youknowriad + + = 18.2.0 = ## Changelog diff --git a/docs/contributors/code/e2e/README.md b/docs/contributors/code/e2e/README.md index 43443cddd6aeb6..3a123cc2988b7a 100644 --- a/docs/contributors/code/e2e/README.md +++ b/docs/contributors/code/e2e/README.md @@ -36,6 +36,7 @@ xvfb-run npm run test:e2e # Only run webkit tests. xvfb-run -- npm run test:e2e -- --project=webkit ``` +If you're already editing in VS Code, you may find the [Playwright extension](https://playwright.dev/docs/getting-started-vscode) helpful for running, writing and debugging tests. ## Best practices diff --git a/docs/getting-started/faq.md b/docs/getting-started/faq.md index 57a379a9fafe13..2bc7fc8f0bf8be 100644 --- a/docs/getting-started/faq.md +++ b/docs/getting-started/faq.md @@ -224,6 +224,11 @@ This is the canonical list of keyboard shortcuts: / / + + Create a group block from the selected multiple blocks. + Ctrl+G + G + Remove multiple selected blocks. delbackspace diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index 3a0a88048e982f..c08869db34b484 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -415,7 +415,7 @@ Create a bulleted or numbered list. ([Source](https://github.com/WordPress/guten - **Name:** core/list - **Category:** text - **Allowed Blocks:** core/list-item -- **Supports:** __unstablePasteTextInline, anchor, color (background, gradients, link, text), interactivity (clientNavigation), spacing (margin, padding), typography (fontSize, lineHeight), ~~className~~, ~~html~~ +- **Supports:** __unstablePasteTextInline, anchor, color (background, gradients, link, text), interactivity (clientNavigation), spacing (margin, padding), typography (fontSize, lineHeight), ~~html~~ - **Attributes:** ordered, placeholder, reversed, start, type, values ## List item diff --git a/docs/reference-guides/interactivity-api/README.md b/docs/reference-guides/interactivity-api/README.md index 1401c7f9bfecc6..b6e0d639c3fc8b 100644 --- a/docs/reference-guides/interactivity-api/README.md +++ b/docs/reference-guides/interactivity-api/README.md @@ -67,6 +67,19 @@ The Interactivity API provides the `@wordpress/interactivity` Script Module. Jav } ``` +The use of `viewScriptModule` also requires the `--experimental-modules` flag for both the [`build`](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-scripts/#build) and [`start`](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-scripts/#start) scripts of `wp-scripts` to ensure a proper build of the Script Modules. + + +```json +// package.json +{ + "scripts": { + ... + "build": "wp-scripts build --experimental-modules", + "start": "wp-scripts start --experimental-modules" + } +``` + #### Add `wp-interactive` directive to a DOM element To "activate" the Interactivity API in a DOM element (and its children), add the [`wp-interactive`](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-interactivity/packages-interactivity-api-reference/#wp-interactive) directive to the DOM element in the block's `render.php` or `save.js` files. diff --git a/docs/reference-guides/interactivity-api/iapi-about.md b/docs/reference-guides/interactivity-api/iapi-about.md index f09ef77e6211b9..887a284be298fb 100644 --- a/docs/reference-guides/interactivity-api/iapi-about.md +++ b/docs/reference-guides/interactivity-api/iapi-about.md @@ -50,13 +50,13 @@ _Dynamic block example_ Toggle -

+

This element is now visible!

``` -As you can see, directives like `data-wp-on--click` or `data-wp-show` are added as custom HTML attributes. WordPress can process this HTML on the server, handling the directives’ logic and creating the appropriate markup. +As you can see, directives like [`data-wp-on--click`](https://developer.wordpress.org/block-editor/reference-guides/interactivity-api/api-reference/#wp-on) or [`data-wp-bind--hidden`](https://developer.wordpress.org/block-editor/reference-guides/interactivity-api/api-reference/#wp-bind) are added as custom HTML attributes. WordPress can process this HTML on the server, handling the directives’ logic and creating the appropriate markup. ### Backward compatible @@ -136,7 +136,7 @@ store( 'wpmovies', { Toggle -

+

This element is now visible!

@@ -155,15 +155,13 @@ The API has been designed to be as performant as possible: Directives can be added, removed, or modified directly from the HTML. For example, users could use the [`render_block` filter](https://developer.wordpress.org/reference/hooks/render_block/) to modify the HTML and its behavior. -In addition to using built-in directives, users can create custom directives to add any custom behaviors to their HTML. - ### Atomic and composable Each directive controls a small part of the DOM, and you can combine multiple directives to create rich, interactive user experiences. ### Compatible with the existing block development tooling -Using built-in directives does not require a build step and only requires a small runtime. A build step is necessary only when creating custom directives that return JSX. For such use cases, the API works out of the box with common block-building tools like [`wp-scripts`](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-scripts/). +The API works out of the box with standard block-building tools like [`wp-scripts`](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-scripts/). The only requirement for `wp-scripts` to properly build the [Script Modules](https://make.wordpress.org/core/2024/03/04/script-modules-in-6-5/) using the Interactivity API is the use of the --experimental-modules flag for both [`build`](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-scripts/#build) and [`start`](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-scripts/#start) scripts. ### Client-side navigation diff --git a/docs/reference-guides/interactivity-api/iapi-faq.md b/docs/reference-guides/interactivity-api/iapi-faq.md index 2e0e0ce4da4308..fba3603f39169e 100644 --- a/docs/reference-guides/interactivity-api/iapi-faq.md +++ b/docs/reference-guides/interactivity-api/iapi-faq.md @@ -99,7 +99,7 @@ The API has been designed with performance in mind, so it shouldn’t be a probl ## Does it work with the Core Translation API? -As the Interactivity API works perfectly with server-side rendering, you can use all the WordPress APIs including [`__()`](https://developer.wordpress.org/reference/functions/__/) and [`_e()`](https://developer.wordpress.org/reference/functions/_e/). You can use it to translate the text in the HTML (as you normally would) and even use it inside the store when using `wp_initial_state()` on the server side. It might look something like this: +As the Interactivity API works perfectly with server-side rendering, you can use all the WordPress APIs including [`__()`](https://developer.wordpress.org/reference/functions/__/) and [`_e()`](https://developer.wordpress.org/reference/functions/_e/). You can use it to translate the text in the HTML (as you normally would) and even use it inside the store when [using `wp_interactivity_state()` on the server side](https://developer.wordpress.org/block-editor/reference-guides/interactivity-api/api-reference/#setting-the-store). It might look something like this: ```php // render.php @@ -111,8 +111,7 @@ wp_interactivity_state( 'favoriteMovies', array( ) ); ``` -A translation API compatible with script modules (needed for the Interactivity API) is -currently being worked on. Check [#60234](https://core.trac.wordpress.org/ticket/60234) to follow the progress on this work. +A translation API compatible with script modules (needed for the Interactivity API) is currently being worked on. Check [#60234](https://core.trac.wordpress.org/ticket/60234) to follow the progress on this work. ## I’m concerned about XSS; can JavaScript be injected into directives? diff --git a/gutenberg.php b/gutenberg.php index dc7a76c1531ba1..bfac7d69c8bbbb 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.4 * Requires PHP: 7.2 - * Version: 18.3.0-rc.1 + * Version: 18.3.0 * Author: Gutenberg Team * Text Domain: gutenberg * diff --git a/lib/blocks.php b/lib/blocks.php index eed9142db436a0..679219cc6ff774 100644 --- a/lib/blocks.php +++ b/lib/blocks.php @@ -26,7 +26,6 @@ function gutenberg_reregister_core_block_types() { 'form-submit-button', 'group', 'html', - 'list', 'list-item', 'missing', 'more', @@ -76,6 +75,7 @@ function gutenberg_reregister_core_block_types() { 'heading.php' => 'core/heading', 'latest-comments.php' => 'core/latest-comments', 'latest-posts.php' => 'core/latest-posts', + 'list.php' => 'core/list', 'loginout.php' => 'core/loginout', 'media-text.php' => 'core/media-text', 'navigation.php' => 'core/navigation', diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 46d7082912f537..1dec7b164d880b 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -320,7 +320,6 @@ class WP_Theme_JSON_Gutenberg { ), 'background-image' => array( array( 'background', 'backgroundImage', 'url' ), - array( 'background', 'backgroundImage', 'source' ), ), ); diff --git a/lib/compat/wordpress-6.5/blocks.php b/lib/compat/wordpress-6.5/blocks.php index a695d075c0b526..89bb22825103ac 100644 --- a/lib/compat/wordpress-6.5/blocks.php +++ b/lib/compat/wordpress-6.5/blocks.php @@ -65,68 +65,27 @@ function gutenberg_block_bindings_replace_html( $block_content, $block_name, str switch ( $block_type->attributes[ $attribute_name ]['source'] ) { case 'html': case 'rich-text': - $block_reader = new WP_HTML_Tag_Processor( $block_content ); - - // TODO: Support for CSS selectors whenever they are ready in the HTML API. - // In the meantime, support comma-separated selectors by exploding them into an array. - $selectors = explode( ',', $block_type->attributes[ $attribute_name ]['selector'] ); - // Add a bookmark to the first tag to be able to iterate over the selectors. - $block_reader->next_tag(); - $block_reader->set_bookmark( 'iterate-selectors' ); - - // TODO: This shouldn't be needed when the `set_inner_html` function is ready. - // Store the parent tag and its attributes to be able to restore them later in the button. - // The button block has a wrapper while the paragraph and heading blocks don't. - if ( 'core/button' === $block_name ) { - $button_wrapper = $block_reader->get_tag(); - $button_wrapper_attribute_names = $block_reader->get_attribute_names_with_prefix( '' ); - $button_wrapper_attrs = array(); - foreach ( $button_wrapper_attribute_names as $name ) { - $button_wrapper_attrs[ $name ] = $block_reader->get_attribute( $name ); - } + // Hardcode the selectors and processing until the HTML API is able to read CSS selectors and replace inner HTML. + // TODO: Use the HTML API instead. + if ( 'core/paragraph' === $block_name && 'content' === $attribute_name ) { + $selector = 'p'; } - - foreach ( $selectors as $selector ) { - // If the parent tag, or any of its children, matches the selector, replace the HTML. - if ( strcasecmp( $block_reader->get_tag( $selector ), $selector ) === 0 || $block_reader->next_tag( - array( - 'tag_name' => $selector, - ) - ) ) { - $block_reader->release_bookmark( 'iterate-selectors' ); - - // TODO: Use `set_inner_html` method whenever it's ready in the HTML API. - // Until then, it is hardcoded for the paragraph, heading, and button blocks. - // Store the tag and its attributes to be able to restore them later. - $selector_attribute_names = $block_reader->get_attribute_names_with_prefix( '' ); - $selector_attrs = array(); - foreach ( $selector_attribute_names as $name ) { - $selector_attrs[ $name ] = $block_reader->get_attribute( $name ); - } - $selector_markup = "<$selector>" . wp_kses_post( $source_value ) . ""; - $amended_content = new WP_HTML_Tag_Processor( $selector_markup ); - $amended_content->next_tag(); - foreach ( $selector_attrs as $attribute_key => $attribute_value ) { - $amended_content->set_attribute( $attribute_key, $attribute_value ); - } - if ( 'core/paragraph' === $block_name || 'core/heading' === $block_name ) { - return $amended_content->get_updated_html(); - } - if ( 'core/button' === $block_name ) { - $button_markup = "<$button_wrapper>{$amended_content->get_updated_html()}"; - $amended_button = new WP_HTML_Tag_Processor( $button_markup ); - $amended_button->next_tag(); - foreach ( $button_wrapper_attrs as $attribute_key => $attribute_value ) { - $amended_button->set_attribute( $attribute_key, $attribute_value ); - } - return $amended_button->get_updated_html(); - } + if ( 'core/heading' === $block_name && 'content' === $attribute_name ) { + $selector = 'h[1-6]'; + } + if ( 'core/button' === $block_name && 'text' === $attribute_name ) { + // Check if it is a - ); -} - -export default PostVisibility; diff --git a/packages/edit-post/src/components/sidebar/post-visibility/style.scss b/packages/edit-post/src/components/sidebar/post-visibility/style.scss deleted file mode 100644 index 0dd9824e5bde76..00000000000000 --- a/packages/edit-post/src/components/sidebar/post-visibility/style.scss +++ /dev/null @@ -1,5 +0,0 @@ -.edit-post-post-visibility__dialog .editor-post-visibility { - // sidebar width - popover padding - form margin - min-width: $sidebar-width - $grid-unit-20 - $grid-unit-20; - margin: $grid-unit-10; -} diff --git a/packages/edit-post/src/components/visual-editor/index.js b/packages/edit-post/src/components/visual-editor/index.js index 56c6bb0404c2ac..3bb50999c2a929 100644 --- a/packages/edit-post/src/components/visual-editor/index.js +++ b/packages/edit-post/src/components/visual-editor/index.js @@ -13,6 +13,7 @@ import { import { useMemo } from '@wordpress/element'; import { useSelect } from '@wordpress/data'; import { store as blocksStore } from '@wordpress/blocks'; +import { store as blockEditorStore } from '@wordpress/block-editor'; /** * Internal dependencies @@ -32,12 +33,13 @@ export default function VisualEditor( { styles } ) { isBlockBasedTheme, hasV3BlocksOnly, isEditingTemplate, + isZoomedOutView, } = useSelect( ( select ) => { const { isFeatureActive } = select( editPostStore ); const { getEditorSettings, getRenderingMode } = select( editorStore ); const { getBlockTypes } = select( blocksStore ); + const { __unstableGetEditorMode } = select( blockEditorStore ); const editorSettings = getEditorSettings(); - return { isWelcomeGuideVisible: isFeatureActive( 'welcomeGuide' ), renderingMode: getRenderingMode(), @@ -47,6 +49,7 @@ export default function VisualEditor( { styles } ) { } ), isEditingTemplate: select( editorStore ).getCurrentPostType() === 'wp_template', + isZoomedOutView: __unstableGetEditorMode() === 'zoom-out', }; }, [] ); const hasMetaBoxes = useSelect( @@ -60,7 +63,11 @@ export default function VisualEditor( { styles } ) { // Add a constant padding for the typewritter effect. When typing at the // bottom, there needs to be room to scroll up. - if ( ! hasMetaBoxes && renderingMode === 'post-only' ) { + if ( + ! isZoomedOutView && + ! hasMetaBoxes && + renderingMode === 'post-only' + ) { paddingBottom = '40vh'; } diff --git a/packages/edit-post/src/components/visual-editor/use-padding-appender.js b/packages/edit-post/src/components/visual-editor/use-padding-appender.js index c091443495740d..ff342ded90817a 100644 --- a/packages/edit-post/src/components/visual-editor/use-padding-appender.js +++ b/packages/edit-post/src/components/visual-editor/use-padding-appender.js @@ -27,7 +27,7 @@ export function usePaddingAppender() { return; } - // only handle clicks under the last child + // Only handle clicks under the last child. const lastChild = node.lastElementChild; if ( ! lastChild ) { return; @@ -44,6 +44,12 @@ export function usePaddingAppender() { .select( blockEditorStore ) .getBlockOrder( '' ); const lastBlockClientId = blockOrder[ blockOrder.length - 1 ]; + + // Do nothing when only default block appender is present. + if ( ! lastBlockClientId ) { + return; + } + const lastBlock = registry .select( blockEditorStore ) .getBlock( lastBlockClientId ); diff --git a/packages/edit-post/src/style.scss b/packages/edit-post/src/style.scss index d780f4257f25ab..be959f2d174e83 100644 --- a/packages/edit-post/src/style.scss +++ b/packages/edit-post/src/style.scss @@ -2,10 +2,6 @@ @import "./components/header/fullscreen-mode-close/style.scss"; @import "./components/layout/style.scss"; @import "./components/meta-boxes/meta-boxes-area/style.scss"; -@import "./components/sidebar/style.scss"; -@import "./components/sidebar/post-format/style.scss"; -@import "./components/sidebar/post-slug/style.scss"; -@import "./components/sidebar/post-visibility/style.scss"; @import "./components/text-editor/style.scss"; @import "./components/visual-editor/style.scss"; @import "./components/welcome-guide/style.scss"; @@ -39,7 +35,7 @@ body.js.block-editor-page { } // Target the editor UI excluding the visual editor contents, metaboxes and custom fields areas. -.edit-post-header, +.editor-header, .edit-post-text-editor, .editor-sidebar, .editor-post-publish-panel { diff --git a/packages/edit-site/package.json b/packages/edit-site/package.json index dfbcb4321f82f9..1cf07bec6740df 100644 --- a/packages/edit-site/package.json +++ b/packages/edit-site/package.json @@ -72,9 +72,7 @@ "client-zip": "^2.4.4", "clsx": "^2.1.1", "colord": "^2.9.2", - "deepmerge": "^4.3.0", "fast-deep-equal": "^3.1.3", - "is-plain-object": "^5.0.0", "memize": "^2.1.0", "react-autosize-textarea": "^7.1.0" }, diff --git a/packages/edit-site/src/components/app/index.js b/packages/edit-site/src/components/app/index.js index cad76b3ea1fb83..764b188acf6a53 100644 --- a/packages/edit-site/src/components/app/index.js +++ b/packages/edit-site/src/components/app/index.js @@ -2,7 +2,10 @@ * WordPress dependencies */ import { SlotFillProvider } from '@wordpress/components'; -import { UnsavedChangesWarning } from '@wordpress/editor'; +import { + UnsavedChangesWarning, + privateApis as editorPrivateApis, +} from '@wordpress/editor'; import { store as noticesStore } from '@wordpress/notices'; import { useDispatch } from '@wordpress/data'; import { __, sprintf } from '@wordpress/i18n'; @@ -13,10 +16,10 @@ import { privateApis as routerPrivateApis } from '@wordpress/router'; * Internal dependencies */ import Layout from '../layout'; -import { GlobalStylesProvider } from '../global-styles/global-styles-provider'; import { unlock } from '../../lock-unlock'; const { RouterProvider } = unlock( routerPrivateApis ); +const { GlobalStylesProvider } = unlock( editorPrivateApis ); export default function App() { const { createErrorNotice } = useDispatch( noticesStore ); diff --git a/packages/edit-site/src/components/block-editor/editor-canvas.js b/packages/edit-site/src/components/block-editor/editor-canvas.js index 0c8b474e9a9c58..321749fa70c842 100644 --- a/packages/edit-site/src/components/block-editor/editor-canvas.js +++ b/packages/edit-site/src/components/block-editor/editor-canvas.js @@ -25,7 +25,6 @@ import { FOCUSABLE_ENTITIES, NAVIGATION_POST_TYPE, } from '../../utils/constants'; -import { computeIFrameScale } from '../../utils/math'; const { EditorCanvas: EditorCanvasRoot } = unlock( editorPrivateApis ); @@ -41,11 +40,9 @@ function EditorCanvas( { isFocusMode, templateType, canvasMode, - isZoomOutMode, currentPostIsTrashed, } = useSelect( ( select ) => { - const { getBlockCount, __unstableGetEditorMode } = - select( blockEditorStore ); + const { getBlockCount } = select( blockEditorStore ); const { getEditedPostType, getCanvasMode } = unlock( select( editSiteStore ) ); @@ -54,7 +51,6 @@ function EditorCanvas( { return { templateType: _templateType, isFocusMode: FOCUSABLE_ENTITIES.includes( _templateType ), - isZoomOutMode: __unstableGetEditorMode() === 'zoom-out', canvasMode: getCanvasMode(), hasBlocks: !! getBlockCount(), currentPostIsTrashed: @@ -139,17 +135,6 @@ function EditorCanvas( { [ settings.styles, enableResizing, canvasMode, currentPostIsTrashed ] ); - const frameSize = isZoomOutMode ? 20 : undefined; - - const scale = isZoomOutMode - ? ( contentWidth ) => - computeIFrameScale( - { width: 1000, scale: 0.55 }, - { width: 400, scale: 0.9 }, - contentWidth - ) - : undefined; - return ( { !! themeColors && !! themeColors.length && ( diff --git a/packages/edit-site/src/components/global-styles/gradients-palette-panel.js b/packages/edit-site/src/components/global-styles/gradients-palette-panel.js index 9ebc66c87c347e..00eb8db83578db 100644 --- a/packages/edit-site/src/components/global-styles/gradients-palette-panel.js +++ b/packages/edit-site/src/components/global-styles/gradients-palette-panel.js @@ -71,7 +71,7 @@ export default function GradientPalettePanel( { name } ) { return ( { !! themeGradients && !! themeGradients.length && ( diff --git a/packages/edit-site/src/components/global-styles/hooks.js b/packages/edit-site/src/components/global-styles/hooks.js index 6ecb1de03a1446..50032786e39942 100644 --- a/packages/edit-site/src/components/global-styles/hooks.js +++ b/packages/edit-site/src/components/global-styles/hooks.js @@ -9,18 +9,19 @@ import a11yPlugin from 'colord/plugins/a11y'; */ import { store as blocksStore } from '@wordpress/blocks'; import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; +import { privateApis as editorPrivateApis } from '@wordpress/editor'; import { useContext } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ -import { mergeBaseAndUserConfigs } from './global-styles-provider'; import { useCurrentMergeThemeStyleVariationsWithUserConfig } from '../../hooks/use-theme-style-variations/use-theme-style-variations-by-property'; import { getFontFamilies } from './utils'; import { unlock } from '../../lock-unlock'; import { useSelect } from '@wordpress/data'; +const { mergeBaseAndUserConfigs } = unlock( editorPrivateApis ); const { useGlobalSetting, useGlobalStyle, GlobalStylesContext } = unlock( blockEditorPrivateApis ); diff --git a/packages/edit-site/src/components/global-styles/stories/index.story.js b/packages/edit-site/src/components/global-styles/stories/index.story.js index f04387295c458a..66cf468d5e224c 100644 --- a/packages/edit-site/src/components/global-styles/stories/index.story.js +++ b/packages/edit-site/src/components/global-styles/stories/index.story.js @@ -2,15 +2,16 @@ * WordPress dependencies */ import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; +import { privateApis as editorPrivateApis } from '@wordpress/editor'; import { useMemo, useState } from '@wordpress/element'; /** * Internal dependencies */ -import { mergeBaseAndUserConfigs } from '../global-styles-provider'; import { default as GlobalStylesUIComponent } from '../ui'; import { unlock } from '../../../lock-unlock'; +const { mergeBaseAndUserConfigs } = unlock( editorPrivateApis ); const { GlobalStylesContext, ExperimentalBlockEditorProvider } = unlock( blockEditorPrivateApis ); diff --git a/packages/edit-site/src/components/global-styles/typography-example.js b/packages/edit-site/src/components/global-styles/typography-example.js index dfb8920fb5ea27..a491ca57bf5be4 100644 --- a/packages/edit-site/src/components/global-styles/typography-example.js +++ b/packages/edit-site/src/components/global-styles/typography-example.js @@ -5,16 +5,17 @@ import { useContext } from '@wordpress/element'; import { __unstableMotion as motion } from '@wordpress/components'; import { _x } from '@wordpress/i18n'; import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; +import { privateApis as editorPrivateApis } from '@wordpress/editor'; /** * Internal dependencies */ -import { mergeBaseAndUserConfigs } from './global-styles-provider'; import { unlock } from '../../lock-unlock'; import { getFamilyPreviewStyle } from './font-library-modal/utils/preview-styles'; import { getFontFamilies } from './utils'; const { GlobalStylesContext } = unlock( blockEditorPrivateApis ); +const { mergeBaseAndUserConfigs } = unlock( editorPrivateApis ); export default function PreviewTypography( { fontSize, variation } ) { const { base } = useContext( GlobalStylesContext ); diff --git a/packages/edit-site/src/components/global-styles/ui.js b/packages/edit-site/src/components/global-styles/ui.js index b50e09550f7079..09c1a8b341f8fc 100644 --- a/packages/edit-site/src/components/global-styles/ui.js +++ b/packages/edit-site/src/components/global-styles/ui.js @@ -76,7 +76,11 @@ function GlobalStylesActionMenu() { return ( - + { ( { onClose } ) => ( <> diff --git a/packages/edit-site/src/components/global-styles/variations/variation.js b/packages/edit-site/src/components/global-styles/variations/variation.js index 877a0095e22ac6..26f5235dece6fc 100644 --- a/packages/edit-site/src/components/global-styles/variations/variation.js +++ b/packages/edit-site/src/components/global-styles/variations/variation.js @@ -10,13 +10,14 @@ import { useMemo, useContext, useState } from '@wordpress/element'; import { ENTER } from '@wordpress/keycodes'; import { __, sprintf } from '@wordpress/i18n'; import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; +import { privateApis as editorPrivateApis } from '@wordpress/editor'; /** * Internal dependencies */ -import { mergeBaseAndUserConfigs } from '../global-styles-provider'; import { unlock } from '../../../lock-unlock'; +const { mergeBaseAndUserConfigs } = unlock( editorPrivateApis ); const { GlobalStylesContext, areGlobalStyleConfigsEqual } = unlock( blockEditorPrivateApis ); diff --git a/packages/edit-site/src/components/header-edit-mode/document-tools/index.js b/packages/edit-site/src/components/header-edit-mode/document-tools/index.js deleted file mode 100644 index cd7720a1a34f97..00000000000000 --- a/packages/edit-site/src/components/header-edit-mode/document-tools/index.js +++ /dev/null @@ -1,68 +0,0 @@ -/** - * WordPress dependencies - */ -import { useViewportMatch } from '@wordpress/compose'; -import { store as blockEditorStore } from '@wordpress/block-editor'; -import { useSelect, useDispatch } from '@wordpress/data'; -import { __ } from '@wordpress/i18n'; -import { chevronUpDown } from '@wordpress/icons'; -import { Button, ToolbarItem } from '@wordpress/components'; -import { - store as editorStore, - privateApis as editorPrivateApis, -} from '@wordpress/editor'; - -/** - * Internal dependencies - */ -import { unlock } from '../../../lock-unlock'; - -const { DocumentTools: EditorDocumentTools } = unlock( editorPrivateApis ); - -export default function DocumentTools( { - blockEditorMode, - hasFixedToolbar, - isDistractionFree, -} ) { - const { isVisualMode } = useSelect( ( select ) => { - const { getEditorMode } = select( editorStore ); - - return { - isVisualMode: getEditorMode() === 'visual', - }; - }, [] ); - const { __unstableSetEditorMode } = useDispatch( blockEditorStore ); - const { setDeviceType } = useDispatch( editorStore ); - const isLargeViewport = useViewportMatch( 'medium' ); - const isZoomedOutViewExperimentEnabled = - window?.__experimentalEnableZoomedOutView && isVisualMode; - const isZoomedOutView = blockEditorMode === 'zoom-out'; - - return ( - - { isZoomedOutViewExperimentEnabled && - isLargeViewport && - ! isDistractionFree && - ! hasFixedToolbar && ( - { - setDeviceType( 'Desktop' ); - __unstableSetEditorMode( - isZoomedOutView ? 'edit' : 'zoom-out' - ); - } } - size="compact" - /> - ) } - - ); -} diff --git a/packages/edit-site/src/components/header-edit-mode/index.js b/packages/edit-site/src/components/header-edit-mode/index.js index 4ae9db1fea80c5..fcd4ea1b388028 100644 --- a/packages/edit-site/src/components/header-edit-mode/index.js +++ b/packages/edit-site/src/components/header-edit-mode/index.js @@ -1,190 +1,51 @@ -/** - * External dependencies - */ -import clsx from 'clsx'; - /** * WordPress dependencies */ -import { useViewportMatch, useReducedMotion } from '@wordpress/compose'; -import { store as blockEditorStore } from '@wordpress/block-editor'; +import { privateApis as editorPrivateApis } from '@wordpress/editor'; import { useSelect } from '@wordpress/data'; -import { useState } from '@wordpress/element'; -import { __unstableMotion as motion } from '@wordpress/components'; -import { store as preferencesStore } from '@wordpress/preferences'; -import { - DocumentBar, - PostSavedState, - store as editorStore, - privateApis as editorPrivateApis, -} from '@wordpress/editor'; /** * Internal dependencies */ -import SiteEditorMoreMenuItems from './more-menu'; +import SiteEditorMoreMenu from './more-menu'; +import { unlock } from '../../lock-unlock'; import SaveButton from '../save-button'; -import DocumentTools from './document-tools'; -import { store as editSiteStore } from '../../store'; +import { isPreviewingTheme } from '../../utils/is-previewing-theme'; import { getEditorCanvasContainerTitle, useHasEditorCanvasContainer, } from '../editor-canvas-container'; -import { unlock } from '../../lock-unlock'; -import { FOCUSABLE_ENTITIES } from '../../utils/constants'; -import { isPreviewingTheme } from '../../utils/is-previewing-theme'; - -const { - CollapsableBlockToolbar, - MoreMenu, - PostViewLink, - PreviewDropdown, - PinnedItems, - PostPublishButtonOrToggle, -} = unlock( editorPrivateApis ); +import { store as editSiteStore } from '../../store'; -export default function HeaderEditMode( { setEntitiesSavedStatesCallback } ) { - const { - templateType, - isDistractionFree, - blockEditorMode, - showIconLabels, - editorCanvasView, - isFixedToolbar, - isPublishSidebarOpened, - } = useSelect( ( select ) => { - const { getEditedPostType } = select( editSiteStore ); - const { __unstableGetEditorMode } = select( blockEditorStore ); - const { get: getPreference } = select( preferencesStore ); - const { getDeviceType } = select( editorStore ); +const { Header: EditorHeader } = unlock( editorPrivateApis ); +function Header( { setEntitiesSavedStatesCallback } ) { + const _isPreviewingTheme = isPreviewingTheme(); + const hasDefaultEditorCanvasView = ! useHasEditorCanvasContainer(); + const { editorCanvasView } = useSelect( ( select ) => { return { - deviceType: getDeviceType(), - templateType: getEditedPostType(), - blockEditorMode: __unstableGetEditorMode(), - showIconLabels: getPreference( 'core', 'showIconLabels' ), editorCanvasView: unlock( select( editSiteStore ) ).getEditorCanvasContainerView(), - isDistractionFree: getPreference( 'core', 'distractionFree' ), - isFixedToolbar: getPreference( 'core', 'fixedToolbar' ), - isPublishSidebarOpened: - select( editorStore ).isPublishSidebarOpened(), }; }, [] ); - const isLargeViewport = useViewportMatch( 'medium' ); - const showTopToolbar = - isLargeViewport && isFixedToolbar && blockEditorMode !== 'zoom-out'; - const disableMotion = useReducedMotion(); - - const hasDefaultEditorCanvasView = ! useHasEditorCanvasContainer(); - - const isFocusMode = FOCUSABLE_ENTITIES.includes( templateType ); - - const isZoomedOutView = blockEditorMode === 'zoom-out'; - - const [ isBlockToolsCollapsed, setIsBlockToolsCollapsed ] = - useState( true ); - - const toolbarVariants = { - isDistractionFree: { y: '-50px' }, - isDistractionFreeHovering: { y: 0 }, - view: { y: 0 }, - edit: { y: 0 }, - }; - - const toolbarTransition = { - type: 'tween', - duration: disableMotion ? 0 : 0.2, - ease: 'easeOut', - }; - - const _isPreviewingTheme = isPreviewingTheme(); return ( -
+ } + forceDisableBlockTools={ ! hasDefaultEditorCanvasView } + title={ + ! hasDefaultEditorCanvasView + ? getEditorCanvasContainerTitle( editorCanvasView ) + : undefined + } > - { hasDefaultEditorCanvasView && ( - - - { showTopToolbar && ( - - ) } - - ) } - - { ! isDistractionFree && ( -
- { ! hasDefaultEditorCanvasView ? ( - getEditorCanvasContainerTitle( editorCanvasView ) - ) : ( - - ) } -
- ) } - -
- - { isLargeViewport && ( -
- -
- ) } - - { - // TODO: For now we conditionally render the Save/Publish buttons based on - // some specific site editor extra handling. Examples are when we're previewing - // a theme, handling of global styles changes or when we're in 'view' mode, - // which opens the save panel in a Modal. - } - { ! _isPreviewingTheme && ! isPublishSidebarOpened && ( - // This button isn't completely hidden by the publish sidebar. - // We can't hide the whole toolbar when the publish sidebar is open because - // we want to prevent mounting/unmounting the PostPublishButtonOrToggle DOM node. - // We track that DOM node to return focus to the PostPublishButtonOrToggle - // when the publish sidebar has been closed. - - ) } - { ! _isPreviewingTheme && ( - - ) } - { _isPreviewingTheme && } - { ! isDistractionFree && } - - -
-
-
+ + ); } + +export default Header; diff --git a/packages/edit-site/src/components/header-edit-mode/style.scss b/packages/edit-site/src/components/header-edit-mode/style.scss index 5963a1c8151410..69b1e9dff38492 100644 --- a/packages/edit-site/src/components/header-edit-mode/style.scss +++ b/packages/edit-site/src/components/header-edit-mode/style.scss @@ -1,201 +1,3 @@ -.edit-site-header-edit-mode { - height: $header-height; - align-items: center; - background-color: $white; - color: $gray-900; - display: flex; - box-sizing: border-box; - width: 100%; - justify-content: space-between; - border-bottom: $border-width solid $gray-200; +.editor-header { padding-left: $header-height; - - // When top toolbar is engaged and should expand fully. - &.show-block-toolbar { - - .edit-site-header-edit-mode__start, - .edit-site-header-edit-mode__end { - flex-basis: auto; - } - - .edit-site-header-edit-mode__center { - display: none; - } - } - - .edit-site-header-edit-mode__start { - display: flex; - border: none; - align-items: center; - flex-grow: 1; - flex-shrink: 2; - // Take up the full height of the header so the border focus - // is visible on toolbar buttons. - height: 100%; - // Allow focus ring to be fully visible on furthest right button. - @include break-medium() { - padding-right: var(--wp-admin-border-width-focus); - // Account for the site hub, which is 60x60px. - flex-basis: calc(37.5% - 60px); - // We need this to be overflow hidden so the block toolbar can - // overflow scroll. If the overflow is visible, flexbox allows - // the toolbar to grow outside of the allowed container space. - overflow: hidden; - } - } - - .edit-site-header-edit-mode__end { - display: flex; - justify-content: flex-end; - height: 100%; - flex-grow: 1; - flex-shrink: 1; - - @include break-medium() { - flex-basis: 37.5%; - } - } - - .edit-site-header-edit-mode__center { - align-items: center; - display: flex; - flex-basis: 100%; - flex-grow: 1; - flex-shrink: 2; - height: 100%; - justify-content: center; - - // Flex items will, by default, refuse to shrink below a minimum - // intrinsic width. In order to shrink this flexbox item, and - // subsequently truncate child text, we set an explicit min-width. - // See https://dev.w3.org/csswg/css-flexbox/#min-size-auto - min-width: 0; - - @include break-medium() { - flex-basis: 25%; - } - } - -} - -.edit-site-header-edit-mode__toolbar { - align-items: center; - display: flex; - gap: $grid-unit-10; - padding-left: $grid-unit-20; - - @include break-medium() { - padding-left: $grid-unit-50 * 0.5; - } - - @include break-wide() { - padding-right: $grid-unit-10; - } - - .edit-site-header-edit-mode__inserter-toggle { - svg { - transition: transform cubic-bezier(0.165, 0.84, 0.44, 1) 0.2s; - @include reduce-motion("transition"); - } - - &.is-pressed { - svg { - transform: rotate(45deg); - } - } - } -} - -/** - * Buttons on the right side - */ - -.edit-site-header-edit-mode__actions { - display: inline-flex; - align-items: center; - flex-wrap: nowrap; - // Ensure actions do not press against .edit-site-header-edit-mode__center. - padding-left: $grid-unit-10; - padding-right: $grid-unit-10; - gap: $grid-unit-10; -} - -.edit-site-header-edit-mode__preview-options { - opacity: 1; - transition: opacity 0.3s; - - &.is-zoomed-out { - opacity: 0; - } -} - -// Button text label styles - -.edit-site-header-edit-mode.show-icon-labels { - .components-button.has-icon { - width: auto; - - // Hide the button icons when labels are set to display... - svg { - display: none; - } - // ... and display labels. - &::after { - content: attr(aria-label); - } - &[aria-disabled="true"] { - background-color: transparent; - } - } - .is-tertiary { - &:active { - box-shadow: 0 0 0 1.5px var(--wp-admin-theme-color); - background-color: transparent; - } - } - // Some margins and padding have to be adjusted so the buttons can still fit on smaller screens. - .edit-site-save-button__button { - padding-left: 6px; - padding-right: 6px; - } - - // The template details toggle has a custom label, different from its aria-label, so we don't want to display both. - .edit-site-document-actions__get-info.edit-site-document-actions__get-info.edit-site-document-actions__get-info { - &::after { - content: none; - } - } - - .edit-site-header-edit-mode__inserter-toggle.edit-site-header-edit-mode__inserter-toggle, - .edit-site-document-actions__get-info.edit-site-document-actions__get-info.edit-site-document-actions__get-info { - height: 36px; - padding: 0 $grid-unit-10; - } - - .block-editor-block-mover { - // Modified group borders. - border-left: none; - - &::before { - content: ""; - width: $border-width; - height: $grid-unit-30; - background-color: $gray-300; - margin-top: $grid-unit-05; - margin-left: $grid-unit; - } - - // Modified block movers horizontal separator. - .block-editor-block-mover__move-button-container { - &::before { - width: calc(100% - #{$grid-unit-30}); - background: $gray-300; - left: calc(50% + 1px); - } - } - } -} - -.components-popover.more-menu-dropdown__content { - z-index: z-index(".components-popover.more-menu__content"); } diff --git a/packages/edit-site/src/components/layout/index.js b/packages/edit-site/src/components/layout/index.js index 9a3c3100c4be7a..d8b0d85e484edb 100644 --- a/packages/edit-site/src/components/layout/index.js +++ b/packages/edit-site/src/components/layout/index.js @@ -72,7 +72,6 @@ export default function Layout() { const { isDistractionFree, - isZoomOutMode, hasFixedToolbar, hasBlockSelected, canvasMode, @@ -104,9 +103,6 @@ export default function Layout() { 'core', 'showBlockBreadcrumbs' ), - isZoomOutMode: - select( blockEditorStore ).__unstableGetEditorMode() === - 'zoom-out', hasBlockSelected: select( blockEditorStore ).getBlockSelectionStart(), }; @@ -186,7 +182,6 @@ export default function Layout() { 'is-full-canvas': canvasMode === 'edit', 'has-fixed-toolbar': hasFixedToolbar, 'is-block-toolbar-visible': hasBlockSelected, - 'is-zoom-out': isZoomOutMode, 'has-block-breadcrumbs': hasBlockBreadcrumbs && ! isDistractionFree && diff --git a/packages/edit-site/src/components/layout/router.js b/packages/edit-site/src/components/layout/router.js index fc19ca9242eace..20d5b463852ff3 100644 --- a/packages/edit-site/src/components/layout/router.js +++ b/packages/edit-site/src/components/layout/router.js @@ -31,7 +31,7 @@ export default function useLayoutAreas() { const isSiteEditorLoading = useIsSiteEditorLoading(); const history = useHistory(); const { params } = useLocation(); - const { postType, postId, path, layout, isCustom, canvas } = params ?? {}; + const { postType, postId, path, layout, isCustom, canvas } = params; // Note: Since "sidebar" is not yet supported here, // returning undefined from "mobile" means show the sidebar. @@ -45,6 +45,7 @@ export default function useLayoutAreas() { sidebar: ( } /> ), @@ -78,13 +79,33 @@ export default function useLayoutAreas() { if ( postType && postId ) { let sidebar; if ( postType === 'wp_template_part' || postType === 'wp_block' ) { - sidebar = ; + sidebar = ( + + ); } else if ( postType === 'wp_template' ) { - sidebar = ; + sidebar = ( + + ); } else if ( postType === 'page' ) { - sidebar = ; + sidebar = ( + + ); } else { - sidebar = ; + sidebar = ( + + ); } return { key: 'page', @@ -104,7 +125,9 @@ export default function useLayoutAreas() { return { key: 'templates-list', areas: { - sidebar: , + sidebar: ( + + ), content: , preview: isListLayout && ( @@ -125,7 +148,7 @@ export default function useLayoutAreas() { return { key: 'patterns', areas: { - sidebar: , + sidebar: , content: , mobile: , }, @@ -137,7 +160,9 @@ export default function useLayoutAreas() { return { key: 'styles', areas: { - sidebar: , + sidebar: ( + + ), preview: , mobile: canvas === 'edit' && ( @@ -152,7 +177,11 @@ export default function useLayoutAreas() { return { key: 'navigation', areas: { - sidebar: , + sidebar: ( + + ), preview: , mobile: canvas === 'edit' && ( @@ -163,7 +192,9 @@ export default function useLayoutAreas() { return { key: 'navigation', areas: { - sidebar: , + sidebar: ( + + ), preview: , mobile: canvas === 'edit' && ( diff --git a/packages/edit-site/src/components/layout/style.scss b/packages/edit-site/src/components/layout/style.scss index 00b67f00f6f23d..55a071caacac41 100644 --- a/packages/edit-site/src/components/layout/style.scss +++ b/packages/edit-site/src/components/layout/style.scss @@ -165,8 +165,9 @@ position: relative; color: $white; border-radius: 0; - height: $header-height; + height: $header-height + $border-width; width: $header-height; + margin-bottom: - $border-width; overflow: hidden; padding: 0; display: flex; diff --git a/packages/edit-site/src/components/revisions/index.js b/packages/edit-site/src/components/revisions/index.js index 2d4f3327035858..b726e79b15f2f7 100644 --- a/packages/edit-site/src/components/revisions/index.js +++ b/packages/edit-site/src/components/revisions/index.js @@ -10,6 +10,7 @@ import { __unstableEditorStyles as EditorStyles, __unstableIframe as Iframe, } from '@wordpress/block-editor'; +import { privateApis as editorPrivateApis } from '@wordpress/editor'; import { useSelect } from '@wordpress/data'; import { useContext, useMemo } from '@wordpress/element'; @@ -18,7 +19,6 @@ import { useContext, useMemo } from '@wordpress/element'; */ import { unlock } from '../../lock-unlock'; -import { mergeBaseAndUserConfigs } from '../global-styles/global-styles-provider'; import EditorCanvasContainer from '../editor-canvas-container'; const { @@ -26,6 +26,7 @@ const { GlobalStylesContext, useGlobalStylesOutputWithConfig, } = unlock( blockEditorPrivateApis ); +const { mergeBaseAndUserConfigs } = unlock( editorPrivateApis ); function isObjectEmpty( object ) { return ! object || Object.keys( object ).length === 0; diff --git a/packages/edit-site/src/components/sidebar-edit-mode/global-styles-sidebar.js b/packages/edit-site/src/components/sidebar-edit-mode/global-styles-sidebar.js index f339b347847416..3254f594632c4d 100644 --- a/packages/edit-site/src/components/sidebar-edit-mode/global-styles-sidebar.js +++ b/packages/edit-site/src/components/sidebar-edit-mode/global-styles-sidebar.js @@ -136,7 +136,10 @@ export default function GlobalStylesSidebar() { closeLabel={ __( 'Close Styles' ) } panelClassName="edit-site-global-styles-sidebar__panel" header={ - +

{ __( 'Styles' ) } @@ -151,6 +154,7 @@ export default function GlobalStylesSidebar() { } disabled={ shouldClearCanvasContainerView } onClick={ toggleStyleBook } + size="compact" /> @@ -162,6 +166,7 @@ export default function GlobalStylesSidebar() { isPressed={ isRevisionsOpened || isRevisionsStyleBookOpened } + size="compact" /> diff --git a/packages/edit-site/src/components/sidebar-edit-mode/settings-header/style.scss b/packages/edit-site/src/components/sidebar-edit-mode/settings-header/style.scss index d74432451e1d4c..c6157519526162 100644 --- a/packages/edit-site/src/components/sidebar-edit-mode/settings-header/style.scss +++ b/packages/edit-site/src/components/sidebar-edit-mode/settings-header/style.scss @@ -1,6 +1,5 @@ .components-panel__header.edit-site-sidebar-edit-mode__panel-tabs { padding-left: 0; - padding-right: $grid-unit-20; .components-button.has-icon { padding: 0; diff --git a/packages/edit-site/src/components/sidebar-edit-mode/style.scss b/packages/edit-site/src/components/sidebar-edit-mode/style.scss index 186908c88a54b2..1f631eb24057c9 100644 --- a/packages/edit-site/src/components/sidebar-edit-mode/style.scss +++ b/packages/edit-site/src/components/sidebar-edit-mode/style.scss @@ -38,14 +38,6 @@ margin: 0; } -.edit-site-global-styles-sidebar .interface-complementary-area-header .components-button.has-icon { - margin-left: 0; -} - -.edit-site-global-styles-sidebar__reset-button.components-button { - margin-left: auto; -} - .edit-site-global-styles-sidebar .components-navigation__menu-title-heading { font-size: $default-font-size * 1.2; font-weight: 500; @@ -104,6 +96,3 @@ } } -.edit-site-sidebar__panel { - margin-top: -1px; -} diff --git a/packages/edit-site/src/components/sidebar-navigation-item/index.js b/packages/edit-site/src/components/sidebar-navigation-item/index.js index 69a2af4a45de42..8e979e7ab8bde7 100644 --- a/packages/edit-site/src/components/sidebar-navigation-item/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-item/index.js @@ -35,7 +35,7 @@ export default function SidebarNavigationItem( { ...props } ) { const history = useHistory(); - const navigate = useContext( SidebarNavigationContext ); + const { navigate } = useContext( SidebarNavigationContext ); // If there is no custom click handler, create one that navigates to `path`. function handleClick( e ) { diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-global-styles/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-global-styles/index.js index f0585b410e40dc..87a7b6d9d15f27 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-global-styles/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-global-styles/index.js @@ -103,7 +103,7 @@ function SidebarNavigationScreenGlobalStylesContent() { ); } -export default function SidebarNavigationScreenGlobalStyles() { +export default function SidebarNavigationScreenGlobalStyles( { backPath } ) { const { revisions, isLoading: isLoadingRevisions } = useGlobalStylesRevisions(); const { openGeneralSidebar } = useDispatch( editSiteStore ); @@ -179,6 +179,7 @@ export default function SidebarNavigationScreenGlobalStyles() { description={ __( 'Choose a different style combination for the theme styles.' ) } + backPath={ backPath } content={ } footer={ shouldShowGlobalStylesFooter && ( diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/index.js index 2fcac0250c2bf5..24fb9372b4fff0 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/index.js @@ -22,7 +22,7 @@ const { useLocation } = unlock( routerPrivateApis ); export const postType = `wp_navigation`; -export default function SidebarNavigationScreenNavigationMenu() { +export default function SidebarNavigationScreenNavigationMenu( { backPath } ) { const { params: { postId }, } = useLocation(); @@ -67,6 +67,7 @@ export default function SidebarNavigationScreenNavigationMenu() { description={ __( 'Navigation Menus are a curated collection of blocks that allow visitors to get around your site.' ) } + backPath={ backPath } > @@ -77,6 +78,7 @@ export default function SidebarNavigationScreenNavigationMenu() { return ( ); } @@ -93,6 +95,7 @@ export default function SidebarNavigationScreenNavigationMenu() { onDuplicate={ _handleDuplicate } /> } + backPath={ backPath } title={ buildNavigationLabel( navigationMenu?.title, navigationMenu?.id, @@ -106,6 +109,7 @@ export default function SidebarNavigationScreenNavigationMenu() { return ( } + backPath={ backPath } title={ buildNavigationLabel( navigationMenu?.title, navigationMenu?.id, diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/index.js index 65f80349ddaca2..d5c3fa92c902f9 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/index.js @@ -46,7 +46,7 @@ function buildMenuLabel( title, id, status ) { // Save a boolean to prevent us creating a fallback more than once per session. let hasCreatedFallback = false; -export default function SidebarNavigationScreenNavigationMenus() { +export default function SidebarNavigationScreenNavigationMenus( { backPath } ) { const { records: navigationMenus, isResolving: isResolvingNavigationMenus, @@ -87,7 +87,7 @@ export default function SidebarNavigationScreenNavigationMenus() { if ( isLoading ) { return ( - + ); @@ -97,6 +97,7 @@ export default function SidebarNavigationScreenNavigationMenus() { return ( ); } @@ -106,6 +107,7 @@ export default function SidebarNavigationScreenNavigationMenus() { return ( handleDelete( firstNavigationMenu ) } handleDuplicate={ () => handleDuplicate( firstNavigationMenu ) } handleSave={ ( edits ) => @@ -116,7 +118,7 @@ export default function SidebarNavigationScreenNavigationMenus() { } return ( - + { navigationMenus?.map( ( { id, title, status }, index ) => ( ); diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/leaf-more-menu.js b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/leaf-more-menu.js index 1e632256418959..bb34802ebbbc36 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/leaf-more-menu.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/leaf-more-menu.js @@ -18,10 +18,6 @@ const POPOVER_PROPS = { /** * Internal dependencies */ -import { - isPreviewingTheme, - currentlyPreviewingTheme, -} from '../../utils/is-previewing-theme'; import { unlock } from '../../lock-unlock'; const { useLocation, useHistory } = unlock( routerPrivateApis ); @@ -68,9 +64,6 @@ export default function LeafMoreMenu( props ) { { postType: attributes.type, postId: attributes.id, - ...( isPreviewingTheme() && { - wp_theme_preview: currentlyPreviewingTheme(), - } ), }, { backPath: params, @@ -82,9 +75,6 @@ export default function LeafMoreMenu( props ) { { postType: 'page', postId: attributes.id, - ...( isPreviewingTheme() && { - wp_theme_preview: currentlyPreviewingTheme(), - } ), }, { backPath: params, diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-pattern/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-pattern/index.js index 6058b7d907d821..1b21466576f8ef 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-pattern/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-pattern/index.js @@ -19,11 +19,11 @@ import TemplateActions from '../template-actions'; const { useLocation, useHistory } = unlock( routerPrivateApis ); -export default function SidebarNavigationScreenPattern() { +export default function SidebarNavigationScreenPattern( { backPath } ) { const history = useHistory(); const location = useLocation(); const { - params: { postType, postId, categoryId, categoryType }, + params: { postType, postId }, } = location; const { setCanvasMode } = unlock( useDispatch( editSiteStore ) ); @@ -31,12 +31,6 @@ export default function SidebarNavigationScreenPattern() { const patternDetails = usePatternDetails( postType, postId ); - const backPath = { - categoryId, - categoryType, - path: '/patterns', - }; - return ( } content={ <> diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-template/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-template/index.js index ff12434b278952..76641f969fca79 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-template/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-template/index.js @@ -94,7 +94,7 @@ function useTemplateDetails( postType, postId ) { return { title, description, content, footer }; } -export default function SidebarNavigationScreenTemplate() { +export default function SidebarNavigationScreenTemplate( { backPath } ) { const history = useHistory(); const { params: { postType, postId }, @@ -108,6 +108,7 @@ export default function SidebarNavigationScreenTemplate() { return ( { - const backPath = - backPathProp ?? - location.state?.backPath ?? - getBackPath( location.params ); history.push( backPath ); navigate( 'back' ); } } diff --git a/packages/edit-site/src/components/sidebar/index.js b/packages/edit-site/src/components/sidebar/index.js index a246acaa46ce78..84820952e1b621 100644 --- a/packages/edit-site/src/components/sidebar/index.js +++ b/packages/edit-site/src/components/sidebar/index.js @@ -7,62 +7,86 @@ import clsx from 'clsx'; * WordPress dependencies */ import { - useCallback, createContext, + useContext, useState, useRef, - useEffect, + useLayoutEffect, } from '@wordpress/element'; import { focus } from '@wordpress/dom'; export const SidebarNavigationContext = createContext( () => {} ); +// Focus a sidebar element after a navigation. The element to focus is either +// specified by `focusSelector` (when navigating back) or it is the first +// tabbable element (usually the "Back" button). +function focusSidebarElement( el, direction, focusSelector ) { + let elementToFocus; + if ( direction === 'back' && focusSelector ) { + elementToFocus = el.querySelector( focusSelector ); + } + if ( direction !== null && ! elementToFocus ) { + const [ firstTabbable ] = focus.tabbable.find( el ); + elementToFocus = firstTabbable ?? el; + } + elementToFocus?.focus(); +} -export default function SidebarContent( { routeKey, children } ) { - const [ navState, setNavState ] = useState( { +// Navigation state that is updated when navigating back or forward. Helps us +// manage the animations and also focus. +function createNavState() { + let state = { direction: null, focusSelector: null, - } ); + }; - const navigate = useCallback( ( direction, focusSelector = null ) => { - setNavState( ( prevState ) => ( { - direction, - focusSelector: - direction === 'forward' && focusSelector - ? focusSelector - : prevState.focusSelector, - } ) ); - }, [] ); + return { + get() { + return state; + }, + navigate( direction, focusSelector = null ) { + state = { + direction, + focusSelector: + direction === 'forward' && focusSelector + ? focusSelector + : state.focusSelector, + }; + }, + }; +} +function SidebarContentWrapper( { children } ) { + const navState = useContext( SidebarNavigationContext ); const wrapperRef = useRef(); - useEffect( () => { - let elementToFocus; - if ( navState.direction === 'back' && navState.focusSelector ) { - elementToFocus = wrapperRef.current.querySelector( - navState.focusSelector - ); - } - if ( navState.direction !== null && ! elementToFocus ) { - const [ firstTabbable ] = focus.tabbable.find( wrapperRef.current ); - elementToFocus = firstTabbable ?? wrapperRef.current; - } - elementToFocus?.focus(); + const [ navAnimation, setNavAnimation ] = useState( null ); + + useLayoutEffect( () => { + const { direction, focusSelector } = navState.get(); + focusSidebarElement( wrapperRef.current, direction, focusSelector ); + setNavAnimation( direction ); }, [ navState ] ); const wrapperCls = clsx( 'edit-site-sidebar__screen-wrapper', { - 'slide-from-left': navState.direction === 'back', - 'slide-from-right': navState.direction === 'forward', + 'slide-from-left': navAnimation === 'back', + 'slide-from-right': navAnimation === 'forward', } ); return ( - +
+ { children } +
+ ); +} + +export default function SidebarContent( { routeKey, children } ) { + const [ navState ] = useState( createNavState ); + + return ( +
-
+ { children } -
+
); diff --git a/packages/edit-site/src/components/style-book/index.js b/packages/edit-site/src/components/style-book/index.js index c807c11ca369e2..900fa47beb99c2 100644 --- a/packages/edit-site/src/components/style-book/index.js +++ b/packages/edit-site/src/components/style-book/index.js @@ -24,6 +24,7 @@ import { __unstableEditorStyles as EditorStyles, __unstableIframe as Iframe, } from '@wordpress/block-editor'; +import { privateApis as editorPrivateApis } from '@wordpress/editor'; import { useSelect } from '@wordpress/data'; import { useResizeObserver } from '@wordpress/compose'; import { useMemo, useState, memo, useContext } from '@wordpress/element'; @@ -34,7 +35,6 @@ import { ENTER, SPACE } from '@wordpress/keycodes'; */ import { unlock } from '../../lock-unlock'; import EditorCanvasContainer from '../editor-canvas-container'; -import { mergeBaseAndUserConfigs } from '../global-styles/global-styles-provider'; const { ExperimentalBlockEditorProvider, @@ -42,6 +42,7 @@ const { GlobalStylesContext, useGlobalStylesOutputWithConfig, } = unlock( blockEditorPrivateApis ); +const { mergeBaseAndUserConfigs } = unlock( editorPrivateApis ); const { CompositeV2: Composite, diff --git a/packages/edit-site/src/components/template-part-converter/index.js b/packages/edit-site/src/components/template-part-converter/index.js index 7694735cbb3025..de47eb6ae26f4d 100644 --- a/packages/edit-site/src/components/template-part-converter/index.js +++ b/packages/edit-site/src/components/template-part-converter/index.js @@ -27,12 +27,25 @@ export default function TemplatePartConverter() { } function TemplatePartConverterMenuItem( { clientIds, onClose } ) { - const blocks = useSelect( - ( select ) => - select( blockEditorStore ).getBlocksByClientId( clientIds ), + const { isContentOnly, blocks } = useSelect( + ( select ) => { + const { getBlocksByClientId, getBlockEditingMode } = + select( blockEditorStore ); + return { + blocks: getBlocksByClientId( clientIds ), + isContentOnly: + clientIds.length === 1 && + getBlockEditingMode( clientIds[ 0 ] ) === 'contentOnly', + }; + }, [ clientIds ] ); + // Do not show the convert button if the block is in content-only mode. + if ( isContentOnly ) { + return null; + } + // Allow converting a single template part to standard blocks. if ( blocks.length === 1 && blocks[ 0 ]?.name === 'core/template-part' ) { return ( diff --git a/packages/edit-site/src/hooks/use-theme-style-variations/use-theme-style-variations-by-property.js b/packages/edit-site/src/hooks/use-theme-style-variations/use-theme-style-variations-by-property.js index 27c3adf438ad6f..d14cd8f64b2ccd 100644 --- a/packages/edit-site/src/hooks/use-theme-style-variations/use-theme-style-variations-by-property.js +++ b/packages/edit-site/src/hooks/use-theme-style-variations/use-theme-style-variations-by-property.js @@ -5,16 +5,17 @@ import { useSelect } from '@wordpress/data'; import { store as coreStore } from '@wordpress/core-data'; import { useContext, useMemo } from '@wordpress/element'; import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; +import { privateApis as editorPrivateApis } from '@wordpress/editor'; import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ -import { mergeBaseAndUserConfigs } from '../../components/global-styles/global-styles-provider'; import cloneDeep from '../../utils/clone-deep'; import { unlock } from '../../lock-unlock'; const { GlobalStylesContext } = unlock( blockEditorPrivateApis ); +const { mergeBaseAndUserConfigs } = unlock( editorPrivateApis ); /** * Removes all instances of a property from an object. diff --git a/packages/edit-site/src/style.scss b/packages/edit-site/src/style.scss index d0002e1070ce3e..f2b8ebd559f2e8 100644 --- a/packages/edit-site/src/style.scss +++ b/packages/edit-site/src/style.scss @@ -85,13 +85,6 @@ body.js.site-editor-php { .interface-interface-skeleton { top: 0; } - - // Todo: Remove this rule when edit site gets support - // for opening unpinned sidebar items. - .interface-complementary-area__pin-unpin-item.components-button { - display: none; - } - } /** diff --git a/packages/edit-site/src/utils/math.js b/packages/edit-site/src/utils/math.js deleted file mode 100644 index 8c4cca4f5e1118..00000000000000 --- a/packages/edit-site/src/utils/math.js +++ /dev/null @@ -1,93 +0,0 @@ -/** - * @typedef {Object} WPPoint - * @property {number} x The horizontal coordinate. - * @property {number} y The vertical coordinate. - */ - -/** - * Clamps a value to a range. Uses the CSS `clamp()` ordering for arguments. - * - * @param {number} min Minimum range. - * @param {number} value Value to clamp. - * @param {number} max Maximum range. - * - * @return {number} Clamped value. - */ -function clamp( min, value, max ) { - return Math.max( min, Math.min( value, max ) ); -} - -/** - * Evaluates a linear function passing through two points at the given x value. - * - * Example: - * f(x) - * │ ╲ - * │ * (p0) - * │ ╲ - * │ ╲ - * │ (p1) * - * │ ╲ - * └──────────── x - * - * @param {WPPoint} p0 First point. - * @param {WPPoint} p1 Second point. - * @param {number} x Value to evaluate at. - * - * @return {number} Result of the two-point linear function at x. - */ -function twoPointLinearFn( p0, p1, x ) { - return ( ( p1.y - p0.y ) / ( p1.x - p0.x ) ) * ( x - p0.x ) + p0.y; -} - -/** - * Evaluates a two-point linear function at a given x value, clamped to the range of the points. - * - * Example: - * f(x) - * │ ───* (p0) - * │ ╲ - * │ ╲ - * │ (p1) *─── - * └──────────── x - * - * @param {WPPoint} p0 First point. - * @param {WPPoint} p1 Second point. - * @param {number} x Value to evaluate at. - * - * @return {number} Result of the two-point linear function clamped to the range of the points. - */ -function clampedTwoPointLinearFn( p0, p1, x ) { - return clamp( - Math.min( p0.y, p1.y ), - twoPointLinearFn( p0, p1, x ), - Math.max( p0.y, p1.y ) - ); -} - -/** - * Computes the iframe scale using a start and end width/scale pair and the current width. - * - * The scale is clamped outside the points and is linearly interpolated between. - * - * Example: - * scale - * │ ───* (start) - * │ ╲ - * │ ╲ - * │ (end) *─── - * └──────────── width - * - * @param {Object} start First width and scale pair. - * @param {Object} end Second width and scale pair. - * @param {number} currentWidth Current width. - * - * @return {number} The scale of the current width between the two points. - */ -export function computeIFrameScale( start, end, currentWidth ) { - return clampedTwoPointLinearFn( - { x: start.width, y: start.scale }, - { x: end.width, y: end.scale }, - currentWidth - ); -} diff --git a/packages/edit-site/src/utils/use-activate-theme.js b/packages/edit-site/src/utils/use-activate-theme.js index d201f630fd6dcb..e5f9488a3edd8b 100644 --- a/packages/edit-site/src/utils/use-activate-theme.js +++ b/packages/edit-site/src/utils/use-activate-theme.js @@ -23,7 +23,7 @@ const { useHistory, useLocation } = unlock( routerPrivateApis ); */ export function useActivateTheme() { const history = useHistory(); - const location = useLocation(); + const { params } = useLocation(); const { startResolution, finishResolution } = useDispatch( coreStore ); return async () => { @@ -36,9 +36,9 @@ export function useActivateTheme() { startResolution( 'activateTheme' ); await window.fetch( activationURL ); finishResolution( 'activateTheme' ); - const { wp_theme_preview: themePreview, ...params } = - location.params; - history.replace( params ); + // Remove the wp_theme_preview query param: we've finished activating + // the queue and are switching to normal Site Editor. + history.replace( { ...params, wp_theme_preview: undefined } ); } }; } diff --git a/packages/edit-widgets/src/components/header/document-tools/index.js b/packages/edit-widgets/src/components/header/document-tools/index.js index bd1b8fc7adae7d..a0d69cde376cf7 100644 --- a/packages/edit-widgets/src/components/header/document-tools/index.js +++ b/packages/edit-widgets/src/components/header/document-tools/index.js @@ -4,12 +4,9 @@ import { useSelect, useDispatch } from '@wordpress/data'; import { __, _x } from '@wordpress/i18n'; import { Button, ToolbarItem } from '@wordpress/components'; -import { - NavigableToolbar, - store as blockEditorStore, -} from '@wordpress/block-editor'; +import { NavigableToolbar } from '@wordpress/block-editor'; import { listView, plus } from '@wordpress/icons'; -import { useCallback, useRef } from '@wordpress/element'; +import { useCallback } from '@wordpress/element'; import { useViewportMatch } from '@wordpress/compose'; /** @@ -17,61 +14,44 @@ import { useViewportMatch } from '@wordpress/compose'; */ import UndoButton from '../undo-redo/undo'; import RedoButton from '../undo-redo/redo'; -import useLastSelectedWidgetArea from '../../../hooks/use-last-selected-widget-area'; import { store as editWidgetsStore } from '../../../store'; import { unlock } from '../../../lock-unlock'; function DocumentTools() { const isMediumViewport = useViewportMatch( 'medium' ); - const inserterButton = useRef(); - const widgetAreaClientId = useLastSelectedWidgetArea(); - const isLastSelectedWidgetAreaOpen = useSelect( - ( select ) => - select( editWidgetsStore ).getIsWidgetAreaOpen( - widgetAreaClientId - ), - [ widgetAreaClientId ] - ); - const { isInserterOpen, isListViewOpen, listViewToggleRef } = useSelect( - ( select ) => { - const { isInserterOpened, isListViewOpened, getListViewToggleRef } = - unlock( select( editWidgetsStore ) ); - return { - isInserterOpen: isInserterOpened(), - isListViewOpen: isListViewOpened(), - listViewToggleRef: getListViewToggleRef(), - }; - }, - [] - ); - const { setIsWidgetAreaOpen, setIsInserterOpened, setIsListViewOpened } = + + const { + isInserterOpen, + isListViewOpen, + inserterSidebarToggleRef, + listViewToggleRef, + } = useSelect( ( select ) => { + const { + isInserterOpened, + getInserterSidebarToggleRef, + isListViewOpened, + getListViewToggleRef, + } = unlock( select( editWidgetsStore ) ); + return { + isInserterOpen: isInserterOpened(), + isListViewOpen: isListViewOpened(), + inserterSidebarToggleRef: getInserterSidebarToggleRef(), + listViewToggleRef: getListViewToggleRef(), + }; + }, [] ); + const { setIsInserterOpened, setIsListViewOpened } = useDispatch( editWidgetsStore ); - const { selectBlock } = useDispatch( blockEditorStore ); - const handleClick = () => { - if ( isInserterOpen ) { - // Focusing the inserter button closes the inserter popover. - setIsInserterOpened( false ); - } else { - if ( ! isLastSelectedWidgetAreaOpen ) { - // Select the last selected block if hasn't already. - selectBlock( widgetAreaClientId ); - // Open the last selected widget area when opening the inserter. - setIsWidgetAreaOpen( widgetAreaClientId, true ); - } - // The DOM updates resulting from selectBlock() and setIsInserterOpened() calls are applied the - // same tick and pretty much in a random order. The inserter is closed if any other part of the - // app receives focus. If selectBlock() happens to take effect after setIsInserterOpened() then - // the inserter is visible for a brief moment and then gets auto-closed due to focus moving to - // the selected block. - window.requestAnimationFrame( () => setIsInserterOpened( true ) ); - } - }; const toggleListView = useCallback( () => setIsListViewOpened( ! isListViewOpen ), [ setIsListViewOpened, isListViewOpen ] ); + const toggleInserterSidebar = useCallback( + () => setIsInserterOpened( ! isInserterOpen ), + [ setIsInserterOpened, isInserterOpen ] + ); + return ( { event.preventDefault(); } } - onClick={ handleClick } + onClick={ toggleInserterSidebar } icon={ plus } /* translators: button label text should, if possible, be under 16 characters. */ diff --git a/packages/edit-widgets/src/components/layout/style.scss b/packages/edit-widgets/src/components/layout/style.scss index a10665f7cafe7d..4f5bb8834593da 100644 --- a/packages/edit-widgets/src/components/layout/style.scss +++ b/packages/edit-widgets/src/components/layout/style.scss @@ -18,7 +18,7 @@ // Leave space for the close button height: calc(100% - #{$button-size} - #{$grid-unit-10}); - .block-editor-inserter__tab { + .block-editor-inserter-sidebar__header { display: none; } diff --git a/packages/edit-widgets/src/store/private-selectors.js b/packages/edit-widgets/src/store/private-selectors.js index fca6aa5ddb759c..ee2000ac128443 100644 --- a/packages/edit-widgets/src/store/private-selectors.js +++ b/packages/edit-widgets/src/store/private-selectors.js @@ -1,3 +1,7 @@ export function getListViewToggleRef( state ) { return state.listViewToggleRef; } + +export function getInserterSidebarToggleRef( state ) { + return state.inserterSidebarToggleRef; +} diff --git a/packages/edit-widgets/src/store/reducer.js b/packages/edit-widgets/src/store/reducer.js index 64bd6b4e0400e7..3dbc95123a382a 100644 --- a/packages/edit-widgets/src/store/reducer.js +++ b/packages/edit-widgets/src/store/reducer.js @@ -79,8 +79,21 @@ export function listViewToggleRef( state = { current: null } ) { return state; } +/** + * This reducer does nothing aside initializing a ref to the inserter sidebar toggle. + * We will have a unique ref per "editor" instance. + * + * @param {Object} state + * @return {Object} Reference to the inserter sidebar toggle button. + */ +export function inserterSidebarToggleRef( state = { current: null } ) { + return state; +} + export default combineReducers( { blockInserterPanel, + inserterSidebarToggleRef, listViewPanel, + listViewToggleRef, widgetAreasOpenState, } ); diff --git a/packages/editor/package.json b/packages/editor/package.json index bab3a2c29107c0..65fa7deae1828c 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -66,6 +66,8 @@ "@wordpress/wordcount": "file:../wordcount", "clsx": "^2.1.1", "date-fns": "^3.6.0", + "deepmerge": "^4.3.0", + "is-plain-object": "^5.0.0", "memize": "^2.1.0", "react-autosize-textarea": "^7.1.0", "remove-accents": "^0.5.0" diff --git a/packages/editor/src/components/block-settings-menu/content-only-settings-menu.js b/packages/editor/src/components/block-settings-menu/content-only-settings-menu.js new file mode 100644 index 00000000000000..4683dd38593a59 --- /dev/null +++ b/packages/editor/src/components/block-settings-menu/content-only-settings-menu.js @@ -0,0 +1,175 @@ +/** + * WordPress dependencies + */ +import { + BlockSettingsMenuControls, + __unstableBlockSettingsMenuFirstItem as BlockSettingsMenuFirstItem, + store as blockEditorStore, + useBlockDisplayInformation, +} from '@wordpress/block-editor'; +import { store as coreStore } from '@wordpress/core-data'; +import { __experimentalText as Text, MenuItem } from '@wordpress/components'; +import { useSelect, useDispatch } from '@wordpress/data'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import { store as editorStore } from '../../store'; +import { unlock } from '../../lock-unlock'; + +function ContentOnlySettingsMenuItems( { clientId, onClose } ) { + const { entity, onNavigateToEntityRecord } = useSelect( + ( select ) => { + const { + getBlockEditingMode, + getBlockParentsByBlockName, + getSettings, + getBlockAttributes, + } = select( blockEditorStore ); + const contentOnly = + getBlockEditingMode( clientId ) === 'contentOnly'; + if ( ! contentOnly ) { + return {}; + } + const patternParent = getBlockParentsByBlockName( + clientId, + 'core/block', + true + )[ 0 ]; + + let record; + if ( patternParent ) { + record = select( coreStore ).getEntityRecord( + 'postType', + 'wp_block', + getBlockAttributes( patternParent ).ref + ); + } else { + const { getCurrentPostType, getCurrentTemplateId } = + select( editorStore ); + const currentPostType = getCurrentPostType(); + const templateId = getCurrentTemplateId(); + if ( currentPostType === 'page' && templateId ) { + record = select( coreStore ).getEntityRecord( + 'postType', + 'wp_template', + templateId + ); + } + } + return { + entity: record, + onNavigateToEntityRecord: + getSettings().onNavigateToEntityRecord, + }; + }, + [ clientId ] + ); + + if ( ! entity ) { + return ( + + ); + } + + const isPattern = entity.type === 'wp_block'; + + return ( + <> + + { + onNavigateToEntityRecord( { + postId: entity.id, + postType: entity.type, + } ); + } } + > + { isPattern ? __( 'Edit pattern' ) : __( 'Edit template' ) } + + + + { isPattern + ? __( + 'Edit the pattern to move, delete, or make further changes to this block.' + ) + : __( + 'Edit the template to move, delete, or make further changes to this block.' + ) } + + + ); +} + +function TemplateLockContentOnlyMenuItems( { clientId, onClose } ) { + const { contentLockingParent } = useSelect( + ( select ) => { + const { getContentLockingParent } = unlock( + select( blockEditorStore ) + ); + return { + contentLockingParent: getContentLockingParent( clientId ), + }; + }, + [ clientId ] + ); + const blockDisplayInformation = + useBlockDisplayInformation( contentLockingParent ); + // Disable reason: We're using a hook here so it has to be on top-level. + // eslint-disable-next-line @wordpress/no-unused-vars-before-return + const { modifyContentLockBlock, selectBlock } = unlock( + useDispatch( blockEditorStore ) + ); + + if ( ! blockDisplayInformation?.title ) { + return null; + } + + return ( + <> + + { + selectBlock( contentLockingParent ); + modifyContentLockBlock( contentLockingParent ); + onClose(); + } } + > + { __( 'Unlock' ) } + + + + { __( + 'Temporarily unlock the parent block to edit, delete or make further changes to this block.' + ) } + + + ); +} + +export default function ContentOnlySettingsMenu() { + return ( + + { ( { selectedClientIds, onClose } ) => + selectedClientIds.length === 1 && ( + + ) + } + + ); +} diff --git a/packages/editor/src/components/block-settings-menu/content-only-settings-menu.native.js b/packages/editor/src/components/block-settings-menu/content-only-settings-menu.native.js new file mode 100644 index 00000000000000..11cebce87bbba5 --- /dev/null +++ b/packages/editor/src/components/block-settings-menu/content-only-settings-menu.native.js @@ -0,0 +1,4 @@ +// Render nothing in native for now. +export default function ContentOnlySettingsMenu() { + return null; +} diff --git a/packages/editor/src/components/block-settings-menu/style.scss b/packages/editor/src/components/block-settings-menu/style.scss new file mode 100644 index 00000000000000..53fa391d28ef04 --- /dev/null +++ b/packages/editor/src/components/block-settings-menu/style.scss @@ -0,0 +1,4 @@ +.editor-content-only-settings-menu__description { + padding: $grid-unit; + min-width: 235px; +} diff --git a/packages/editor/src/components/document-tools/index.js b/packages/editor/src/components/document-tools/index.js index 64d94cc2e9165f..6952fa34e31ae9 100644 --- a/packages/editor/src/components/document-tools/index.js +++ b/packages/editor/src/components/document-tools/index.js @@ -16,7 +16,7 @@ import { } from '@wordpress/block-editor'; import { Button, ToolbarItem } from '@wordpress/components'; import { listView, plus } from '@wordpress/icons'; -import { useRef, useCallback } from '@wordpress/element'; +import { useCallback } from '@wordpress/element'; import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; import { store as preferencesStore } from '@wordpress/preferences'; @@ -35,11 +35,9 @@ const preventDefault = ( event ) => { function DocumentTools( { className, disableBlockTools = false, - children, // This is a temporary prop until the list view is fully unified between post and site editors. listViewLabel = __( 'Document Overview' ), } ) { - const inserterButton = useRef(); const { setIsInserterOpened, setIsListViewOpened } = useDispatch( editorStore ); const { @@ -47,16 +45,21 @@ function DocumentTools( { isInserterOpened, isListViewOpen, listViewShortcut, + inserterSidebarToggleRef, listViewToggleRef, hasFixedToolbar, showIconLabels, } = useSelect( ( select ) => { const { getSettings } = select( blockEditorStore ); const { get } = select( preferencesStore ); - const { isListViewOpened, getListViewToggleRef } = unlock( - select( editorStore ) - ); + const { + isListViewOpened, + getEditorMode, + getInserterSidebarToggleRef, + getListViewToggleRef, + } = unlock( select( editorStore ) ); const { getShortcutRepresentation } = select( keyboardShortcutsStore ); + const { __unstableGetEditorMode } = select( blockEditorStore ); return { isInserterOpened: select( editorStore ).isInserterOpened(), @@ -64,10 +67,13 @@ function DocumentTools( { listViewShortcut: getShortcutRepresentation( 'core/editor/toggle-list-view' ), + inserterSidebarToggleRef: getInserterSidebarToggleRef(), listViewToggleRef: getListViewToggleRef(), hasFixedToolbar: getSettings().hasFixedToolbar, showIconLabels: get( 'core', 'showIconLabels' ), isDistractionFree: get( 'core', 'distractionFree' ), + isVisualMode: getEditorMode() === 'visual', + isZoomedOutView: __unstableGetEditorMode() === 'zoom-out', }; }, [] ); @@ -82,17 +88,10 @@ function DocumentTools( { [ setIsListViewOpened, isListViewOpen ] ); - const toggleInserter = useCallback( () => { - if ( isInserterOpened ) { - // Focusing the inserter button should close the inserter popover. - // However, there are some cases it won't close when the focus is lost. - // See https://github.com/WordPress/gutenberg/issues/43090 for more details. - inserterButton.current.focus(); - setIsInserterOpened( false ); - } else { - setIsInserterOpened( true ); - } - }, [ isInserterOpened, setIsInserterOpened ] ); + const toggleInserter = useCallback( + () => setIsInserterOpened( ! isInserterOpened ), + [ isInserterOpened, setIsInserterOpened ] + ); /* translators: button label text should, if possible, be under 16 characters. */ const longLabel = _x( @@ -118,7 +117,7 @@ function DocumentTools( {
{ ! isDistractionFree && ( ) } - { children }
); diff --git a/packages/editor/src/components/editor-canvas/index.js b/packages/editor/src/components/editor-canvas/index.js index 8f63ee6980aac1..37f03fc4a1dab7 100644 --- a/packages/editor/src/components/editor-canvas/index.js +++ b/packages/editor/src/components/editor-canvas/index.js @@ -161,13 +161,17 @@ function EditorCanvas( { hasRootPaddingAwareAlignments, themeHasDisabledLayoutStyles, themeSupportsLayout, + isZoomOutMode, } = useSelect( ( select ) => { - const _settings = select( blockEditorStore ).getSettings(); + const { getSettings, __unstableGetEditorMode } = + select( blockEditorStore ); + const _settings = getSettings(); return { themeHasDisabledLayoutStyles: _settings.disableLayoutStyles, themeSupportsLayout: _settings.supportsLayout, hasRootPaddingAwareAlignments: _settings.__experimentalFeatures?.useRootPaddingAwareAlignments, + isZoomOutMode: __unstableGetEditorMode() === 'zoom-out', }; }, [] ); @@ -319,6 +323,13 @@ function EditorCanvas( { } ), ] ); + const zoomOutProps = isZoomOutMode + ? { + scale: 'default', + frameSize: '20px', + } + : {}; + return ( { + const { get: getPreference } = select( preferencesStore ); + const { + getEditorMode, + getEditorSettings, + isPublishSidebarOpened: _isPublishSidebarOpened, + } = select( editorStore ); + const { __unstableGetEditorMode } = select( blockEditorStore ); + + return { + isTextEditor: getEditorMode() === 'text', + isPublishSidebarOpened: _isPublishSidebarOpened(), + showIconLabels: getPreference( 'core', 'showIconLabels' ), + hasFixedToolbar: getPreference( 'core', 'fixedToolbar' ), + isNestedEntity: + !! getEditorSettings().onNavigateToPreviousEntityRecord, + isZoomedOutView: __unstableGetEditorMode() === 'zoom-out', + }; + }, [] ); + + const hasTopToolbar = isLargeViewport && hasFixedToolbar; + + const [ isBlockToolsCollapsed, setIsBlockToolsCollapsed ] = + useState( true ); + + // The edit-post-header classname is only kept for backward compatibilty + // as some plugins might be relying on its presence. + return ( +
+ { children } + + + { hasTopToolbar && ( + + ) } +
+ { ! title ? ( + + + + ) : ( + title + ) } +
+
+ + { ! customSaveButton && ! isPublishSidebarOpened && ( + // This button isn't completely hidden by the publish sidebar. + // We can't hide the whole toolbar when the publish sidebar is open because + // we want to prevent mounting/unmounting the PostPublishButtonOrToggle DOM node. + // We track that DOM node to return focus to the PostPublishButtonOrToggle + // when the publish sidebar has been closed. + + ) } + + + + { ! customSaveButton && ( + + ) } + { customSaveButton } + { ( isWideViewport || ! showIconLabels ) && ( + + ) } + + +
+ ); +} + +export default Header; diff --git a/packages/editor/src/components/header/style.scss b/packages/editor/src/components/header/style.scss new file mode 100644 index 00000000000000..3040362a7bd575 --- /dev/null +++ b/packages/editor/src/components/header/style.scss @@ -0,0 +1,231 @@ +.editor-header { + height: $header-height; + background: $white; + display: flex; + flex-wrap: wrap; + align-items: center; + // The header should never be wider than the viewport, or buttons might be hidden. Especially relevant at high zoom levels. Related to https://core.trac.wordpress.org/ticket/47603#ticket. + max-width: 100vw; + justify-content: space-between; + + // Make toolbar sticky on larger breakpoints + @include break-zoomed-in { + flex-wrap: nowrap; + } +} + +.editor-header__toolbar { + display: flex; + // Allow this area to shrink to fit the toolbar buttons. + flex-shrink: 8; + // Take up the space of the toolbar so it can be justified to the left side of the toolbar. + flex-grow: 3; + // Hide the overflow so flex will limit its width. Block toolbar will allow scrolling on fixed toolbar. + overflow: hidden; + // Leave enough room for the focus ring to show. + padding: 2px 0; + align-items: center; + // Allow focus ring to be fully visible on furthest right button. + @include break-medium() { + padding-right: var(--wp-admin-border-width-focus); + } + + .table-of-contents { + display: none; + + @include break-small() { + display: block; + } + } +} + +.editor-header__center { + flex-grow: 1; + display: flex; + justify-content: center; + + &.is-collapsed { + display: none; + } +} + +/** + * Buttons on the right side + */ + +.editor-header__settings { + display: inline-flex; + align-items: center; + flex-wrap: nowrap; + padding-right: $grid-unit-05; + + @include break-small () { + padding-right: $grid-unit-10; + } + + gap: $grid-unit-10; +} + +/** + * Show icon labels. + */ + +.show-icon-labels.interface-pinned-items, +.show-icon-labels .editor-header { + .components-button.has-icon { + width: auto; + + // Hide the button icons when labels are set to display... + svg { + display: none; + } + // ... and display labels. + &::after { + content: attr(aria-label); + } + &[aria-disabled="true"] { + background-color: transparent; + } + } + .is-tertiary { + &:active { + box-shadow: 0 0 0 1.5px var(--wp-admin-theme-color); + background-color: transparent; + } + } + // Exception for drodpdown toggle buttons. + .components-button.has-icon.button-toggle { + svg { + display: block; + } + &::after { + content: none; + } + } + + // Don't hide MenuItemsChoice check icons + .components-menu-items-choice .components-menu-items__item-icon.components-menu-items__item-icon { + display: block; + } + .editor-document-tools__inserter-toggle.editor-document-tools__inserter-toggle, + .interface-pinned-items .components-button { + padding-left: $grid-unit; + padding-right: $grid-unit; + + @include break-small { + padding-left: $grid-unit-15; + padding-right: $grid-unit-15; + } + } + + .editor-post-save-draft.editor-post-save-draft, + .editor-post-saved-state.editor-post-saved-state { + &::after { + content: none; + } + } +} + +.show-icon-labels { + .editor-header__toolbar .block-editor-block-mover { + // Modified group borders. + border-left: none; + + &::before { + content: ""; + width: $border-width; + height: $grid-unit-30; + background-color: $gray-300; + margin-top: $grid-unit-05; + margin-left: $grid-unit; + } + + // Modified block movers horizontal separator. + .block-editor-block-mover__move-button-container { + &::before { + width: calc(100% - #{$grid-unit-30}); + background: $gray-300; + left: calc(50% + 1px); + } + } + } +} + +.show-icon-labels.interface-pinned-items { + padding: 6px $grid-unit-15 $grid-unit-15; + margin-top: 0; + margin-bottom: 0; + margin-left: -$grid-unit-15; + margin-right: -$grid-unit-15; + border-bottom: 1px solid $gray-400; + display: block; + + > .components-button.has-icon { + margin: 0; + padding: 6px 6px 6px $grid-unit; + width: 14.625rem; + justify-content: flex-start; + + &[aria-expanded="true"] svg { + display: block; + max-width: $grid-unit-30; + } + &[aria-expanded="false"] { + padding-left: $grid-unit-50; + } + svg { + margin-right: 8px; + } + } +} + +.editor-header__post-preview-button { + @include break-small { + display: none; + } +} + +.is-distraction-free { + .interface-interface-skeleton__header { + border-bottom: none; + } + + .editor-header { + background-color: $white; + border-bottom: 1px solid #e0e0e0; + position: absolute; + width: 100%; + + + // hide some parts + & > .edit-post-header__settings > .edit-post-header__post-preview-button { + visibility: hidden; + } + + & > .editor-header__toolbar .editor-document-tools__document-overview-toggle, + & > .editor-header__settings > .editor-preview-dropdown, + & > .editor-header__settings > .interface-pinned-items { + display: none; + } + + } + + // We need ! important because we override inline styles + // set by the motion component. + .interface-interface-skeleton__header:focus-within { + opacity: 1 !important; + div { + transform: translateX(0) translateZ(0) !important; + } + + } + + .components-editor-notices__dismissible { + position: absolute; + z-index: 35; + } +} + +.components-popover.more-menu-dropdown__content { + z-index: z-index(".components-popover.more-menu__content"); +} diff --git a/packages/editor/src/components/inserter-sidebar/index.js b/packages/editor/src/components/inserter-sidebar/index.js index 98292c6662559b..cd45d101f187a3 100644 --- a/packages/editor/src/components/inserter-sidebar/index.js +++ b/packages/editor/src/components/inserter-sidebar/index.js @@ -2,16 +2,14 @@ * WordPress dependencies */ import { useDispatch, useSelect } from '@wordpress/data'; -import { Button } from '@wordpress/components'; -import { __experimentalLibrary as Library } from '@wordpress/block-editor'; -import { closeSmall } from '@wordpress/icons'; import { - useViewportMatch, - __experimentalUseDialog as useDialog, -} from '@wordpress/compose'; -import { __ } from '@wordpress/i18n'; -import { useRef } from '@wordpress/element'; + __experimentalLibrary as Library, + store as blockEditorStore, +} from '@wordpress/block-editor'; +import { useViewportMatch } from '@wordpress/compose'; +import { useCallback, useRef } from '@wordpress/element'; import { store as preferencesStore } from '@wordpress/preferences'; +import { ESCAPE } from '@wordpress/keycodes'; /** * Internal dependencies @@ -23,53 +21,77 @@ export default function InserterSidebar( { closeGeneralSidebar, isRightSidebarOpen, } ) { - const { insertionPoint, showMostUsedBlocks } = useSelect( ( select ) => { - const { getInsertionPoint } = unlock( select( editorStore ) ); + const { + blockSectionRootClientId, + inserterSidebarToggleRef, + insertionPoint, + showMostUsedBlocks, + } = useSelect( ( select ) => { + const { getInserterSidebarToggleRef, getInsertionPoint } = unlock( + select( editorStore ) + ); + const { getBlockRootClientId, __unstableGetEditorMode, getSettings } = + select( blockEditorStore ); const { get } = select( preferencesStore ); + const getBlockSectionRootClientId = () => { + if ( __unstableGetEditorMode() === 'zoom-out' ) { + const { sectionRootClientId } = unlock( getSettings() ); + if ( sectionRootClientId ) { + return sectionRootClientId; + } + } + return getBlockRootClientId(); + }; return { + inserterSidebarToggleRef: getInserterSidebarToggleRef(), insertionPoint: getInsertionPoint(), showMostUsedBlocks: get( 'core', 'mostUsedBlocks' ), + blockSectionRootClientId: getBlockSectionRootClientId(), }; }, [] ); const { setIsInserterOpened } = useDispatch( editorStore ); const isMobileViewport = useViewportMatch( 'medium', '<' ); - const [ inserterDialogRef, inserterDialogProps ] = useDialog( { - onClose: () => setIsInserterOpened( false ), - focusOnMount: true, - } ); - const libraryRef = useRef(); + // When closing the inserter, focus should return to the toggle button. + const closeInserterSidebar = useCallback( () => { + setIsInserterOpened( false ); + inserterSidebarToggleRef.current?.focus(); + }, [ inserterSidebarToggleRef, setIsInserterOpened ] ); + + const closeOnEscape = useCallback( + ( event ) => { + if ( event.keyCode === ESCAPE && ! event.defaultPrevented ) { + event.preventDefault(); + closeInserterSidebar(); + } + }, + [ closeInserterSidebar ] + ); + return ( -
-
-
+ // eslint-disable-next-line jsx-a11y/no-static-element-interactions +
diff --git a/packages/editor/src/components/inserter-sidebar/style.scss b/packages/editor/src/components/inserter-sidebar/style.scss index dbfb774d1f1a68..e3564cbd03aafe 100644 --- a/packages/editor/src/components/inserter-sidebar/style.scss +++ b/packages/editor/src/components/inserter-sidebar/style.scss @@ -6,19 +6,23 @@ flex-direction: column; } -.editor-inserter-sidebar__header { - position: relative; +.block-editor-inserter-sidebar__header { + border-bottom: $border-width solid $gray-300; + padding-right: $grid-unit-10; display: flex; - justify-content: flex-end; - z-index: z-index(".editor-inserter-sidebar__header"); -} + justify-content: space-between; -.editor-inserter-sidebar__close-button { - position: absolute; - top: $grid-unit-15; - right: $grid-unit-15; + .block-editor-inserter-sidebar__close-button { + order: 1; + align-self: center; + } } .editor-inserter-sidebar__content { - height: 100%; + // Leave space for the close button + height: calc(100% - #{$button-size} - #{$grid-unit-10}); + + @include break-medium() { + height: 100%; + } } diff --git a/packages/editor/src/components/post-actions/actions.js b/packages/editor/src/components/post-actions/actions.js index 763b354010bf13..f3bf0f9c5c66bd 100644 --- a/packages/editor/src/components/post-actions/actions.js +++ b/packages/editor/src/components/post-actions/actions.js @@ -43,7 +43,13 @@ const trashPostAction = { }, supportsBulk: true, hideModalHeader: true, - RenderModal: ( { items: posts, closeModal, onActionPerformed } ) => { + RenderModal: ( { + items: posts, + closeModal, + onActionStart, + onActionPerformed, + } ) => { + const [ isBusy, setIsBusy ] = useState( false ); const { createSuccessNotice, createErrorNotice } = useDispatch( noticesStore ); const { deleteEntityRecord } = useDispatch( coreStore ); @@ -67,12 +73,21 @@ const trashPostAction = { ) } - @@ -296,9 +315,9 @@ function useRestorePostAction() { return status === 'trash'; }, async callback( posts, onActionPerformed ) { - try { - for ( const post of posts ) { - await editEntityRecord( + await Promise.allSettled( + posts.map( ( post ) => { + return editEntityRecord( 'postType', post.type, post.id, @@ -306,14 +325,24 @@ function useRestorePostAction() { status: 'draft', } ); - await saveEditedEntityRecord( + } ) + ); + const promiseResult = await Promise.allSettled( + posts.map( ( post ) => { + return saveEditedEntityRecord( 'postType', post.type, post.id, { throwOnError: true } ); - } + } ) + ); + if ( + promiseResult.every( + ( { status } ) => status === 'fulfilled' + ) + ) { createSuccessNotice( posts.length > 1 ? sprintf( @@ -334,25 +363,56 @@ function useRestorePostAction() { if ( onActionPerformed ) { onActionPerformed( posts ); } - } catch ( error ) { + } else { + // If there was at lease one failure. let errorMessage; - if ( - error.message && - error.code !== 'unknown_error' && - error.message - ) { - errorMessage = error.message; - } else if ( posts.length > 1 ) { - errorMessage = __( - 'An error occurred while restoring the posts.' - ); + // If we were trying to move a single post to the trash. + if ( promiseResult.length === 1 ) { + if ( promiseResult[ 0 ].reason?.message ) { + errorMessage = promiseResult[ 0 ].reason.message; + } else { + errorMessage = __( + 'An error occurred while restoring the post.' + ); + } + // If we were trying to move multiple posts to the trash } else { - errorMessage = __( - 'An error occurred while restoring the post.' + const errorMessages = new Set(); + const failedPromises = promiseResult.filter( + ( { status } ) => status === 'rejected' ); + for ( const failedPromise of failedPromises ) { + if ( failedPromise.reason?.message ) { + errorMessages.add( + failedPromise.reason.message + ); + } + } + if ( errorMessages.size === 0 ) { + errorMessage = __( + 'An error occurred while restoring the posts.' + ); + } else if ( errorMessages.size === 1 ) { + errorMessage = sprintf( + /* translators: %s: an error message */ + __( + 'An error occurred while restoring the posts: %s' + ), + [ ...errorMessages ][ 0 ] + ); + } else { + errorMessage = sprintf( + /* translators: %s: a list of comma separated error messages */ + __( + 'Some errors occurred while restoring the posts: %s' + ), + [ ...errorMessages ].join( ',' ) + ); + } } - - createErrorNotice( errorMessage, { type: 'snackbar' } ); + createErrorNotice( errorMessage, { + type: 'snackbar', + } ); } }, } ), @@ -618,9 +678,16 @@ const resetTemplateAction = { id: 'reset-template', label: __( 'Reset' ), isEligible: isTemplateRevertable, + icon: backup, supportsBulk: true, hideModalHeader: true, - RenderModal: ( { items, closeModal, onActionPerformed } ) => { + RenderModal: ( { + items, + closeModal, + onActionStart, + onActionPerformed, + } ) => { + const [ isBusy, setIsBusy ] = useState( false ); const { revertTemplate } = unlock( useDispatch( editorStore ) ); const { saveEditedEntityRecord } = useDispatch( coreStore ); const { createSuccessNotice, createErrorNotice } = @@ -690,16 +757,29 @@ const resetTemplateAction = { { __( 'Reset to default and clear all customizations?' ) } - @@ -730,9 +810,16 @@ const deleteTemplateAction = { id: 'delete-template', label: __( 'Delete' ), isEligible: isTemplateRemovable, + icon: trash, supportsBulk: true, hideModalHeader: true, - RenderModal: ( { items: templates, closeModal, onActionPerformed } ) => { + RenderModal: ( { + items: templates, + closeModal, + onActionStart, + onActionPerformed, + } ) => { + const [ isBusy, setIsBusy ] = useState( false ); const { removeTemplates } = unlock( useDispatch( editorStore ) ); return ( @@ -756,18 +843,31 @@ const deleteTemplateAction = { ) } - @@ -936,8 +1036,7 @@ export function usePostActions( onActionPerformed, actionIds = null ) { RenderModal: ( props ) => { return ( { if ( props.onActionPerformed ) { props.onActionPerformed( diff --git a/packages/edit-post/src/components/sidebar/post-format/index.js b/packages/editor/src/components/post-format/panel.js similarity index 60% rename from packages/edit-post/src/components/sidebar/post-format/index.js rename to packages/editor/src/components/post-format/panel.js index 5127fa0930f3fc..cbd065183eefab 100644 --- a/packages/edit-post/src/components/sidebar/post-format/index.js +++ b/packages/editor/src/components/post-format/panel.js @@ -2,15 +2,17 @@ * WordPress dependencies */ import { PanelRow } from '@wordpress/components'; -import { - PostFormat as PostFormatForm, - PostFormatCheck, -} from '@wordpress/editor'; + +/** + * Internal dependencies + */ +import PostFormatForm from './'; +import PostFormatCheck from './check'; export function PostFormat() { return ( - + diff --git a/packages/editor/src/components/post-format/style.scss b/packages/editor/src/components/post-format/style.scss index 09fb0f11b9f949..135ee7f3579029 100644 --- a/packages/editor/src/components/post-format/style.scss +++ b/packages/editor/src/components/post-format/style.scss @@ -1,3 +1,9 @@ [class].editor-post-format__suggestion { margin: $grid-unit-05 0 0 0; } + +.editor-post-format__panel { + display: flex; + flex-direction: column; + align-items: stretch; +} diff --git a/packages/edit-post/src/components/sidebar/post-slug/index.js b/packages/editor/src/components/post-slug/panel.js similarity index 61% rename from packages/edit-post/src/components/sidebar/post-slug/index.js rename to packages/editor/src/components/post-slug/panel.js index 8b52f94bd33f5d..6ab97a28b251c3 100644 --- a/packages/edit-post/src/components/sidebar/post-slug/index.js +++ b/packages/editor/src/components/post-slug/panel.js @@ -2,12 +2,17 @@ * WordPress dependencies */ import { PanelRow } from '@wordpress/components'; -import { PostSlug as PostSlugForm, PostSlugCheck } from '@wordpress/editor'; + +/** + * Internal dependencies + */ +import PostSlugForm from './'; +import PostSlugCheck from './check'; export function PostSlug() { return ( - + diff --git a/packages/edit-post/src/components/sidebar/post-slug/style.scss b/packages/editor/src/components/post-slug/style.scss similarity index 74% rename from packages/edit-post/src/components/sidebar/post-slug/style.scss rename to packages/editor/src/components/post-slug/style.scss index 067dfcb08d6f06..551450582128e0 100644 --- a/packages/edit-post/src/components/sidebar/post-slug/style.scss +++ b/packages/editor/src/components/post-slug/style.scss @@ -1,4 +1,4 @@ -.edit-post-post-slug { +.editor-post-slug { display: flex; flex-direction: column; align-items: stretch; diff --git a/packages/editor/src/components/post-sticky/panel.js b/packages/editor/src/components/post-sticky/panel.js new file mode 100644 index 00000000000000..b5ede0c1ab8825 --- /dev/null +++ b/packages/editor/src/components/post-sticky/panel.js @@ -0,0 +1,18 @@ +/** + * Internal dependencies + */ +import PostPanelRow from '../post-panel-row'; +import PostStickyForm from './'; +import PostStickyCheck from './check'; + +export function PostStickyPanel() { + return ( + + + + + + ); +} + +export default PostStickyPanel; diff --git a/packages/editor/src/components/post-trash/panel.js b/packages/editor/src/components/post-trash/panel.js new file mode 100644 index 00000000000000..9111c048eb60b6 --- /dev/null +++ b/packages/editor/src/components/post-trash/panel.js @@ -0,0 +1,13 @@ +/** + * Internal dependencies + */ +import PostTrashCheck from './check'; +import PostTrashLink from './'; + +export default function PostTrashPanel() { + return ( + + + + ); +} diff --git a/packages/editor/src/components/provider/index.js b/packages/editor/src/components/provider/index.js index df0fb488c69dc1..4f359104ea02da 100644 --- a/packages/editor/src/components/provider/index.js +++ b/packages/editor/src/components/provider/index.js @@ -28,6 +28,7 @@ import useCommands from '../commands'; import BlockRemovalWarnings from '../block-removal-warnings'; import StartPageOptions from '../start-page-options'; import KeyboardShortcutHelpModal from '../keyboard-shortcut-help-modal'; +import ContentOnlySettingsMenu from '../block-settings-menu/content-only-settings-menu'; const { ExperimentalBlockEditorProvider } = unlock( blockEditorPrivateApis ); const { PatternsMenuItems } = unlock( editPatternsPrivateApis ); @@ -264,6 +265,7 @@ export const ExperimentalEditorProvider = withRegistryProvider( { ! settings.__unstableIsPreviewMode && ( <> + { mode === 'template-locked' && ( ) } diff --git a/packages/editor/src/components/provider/use-block-editor-settings.js b/packages/editor/src/components/provider/use-block-editor-settings.js index bcd08856141831..2a9ecf60732480 100644 --- a/packages/editor/src/components/provider/use-block-editor-settings.js +++ b/packages/editor/src/components/provider/use-block-editor-settings.js @@ -24,8 +24,10 @@ import inserterMediaCategories from '../media-categories'; import { mediaUpload } from '../../utils'; import { store as editorStore } from '../../store'; import { lock, unlock } from '../../lock-unlock'; +import { useGlobalStylesContext } from '../global-styles-provider'; const EMPTY_BLOCKS_LIST = []; +const DEFAULT_STYLES = {}; function __experimentalReusableBlocksSelect( select ) { return ( @@ -173,6 +175,9 @@ function useBlockEditorSettings( settings, postType, postId, renderingMode ) { [ postType, postId, isLargeViewport, renderingMode ] ); + const { merged: mergedGlobalStyles } = useGlobalStylesContext(); + const globalStylesData = mergedGlobalStyles.styles ?? DEFAULT_STYLES; + const settingsBlockPatterns = settings.__experimentalAdditionalBlockPatterns ?? // WP 6.0 settings.__experimentalBlockPatterns; // WP 5.9 @@ -251,6 +256,8 @@ function useBlockEditorSettings( settings, postType, postId, renderingMode ) { }, [ settings.allowedBlockTypes, hiddenBlockTypes, blockTypes ] ); const forceDisableFocusMode = settings.focusMode === false; + const { globalStylesDataKey, selectBlockPatternsKey } = + unlock( privateApis ); return useMemo( () => { const blockEditorSettings = { @@ -259,6 +266,7 @@ function useBlockEditorSettings( settings, postType, postId, renderingMode ) { BLOCK_EDITOR_SETTINGS.includes( key ) ) ), + [ globalStylesDataKey ]: globalStylesData, allowedBlockTypes, allowRightClickOverrides, focusMode: focusMode && ! forceDisableFocusMode, @@ -267,10 +275,14 @@ function useBlockEditorSettings( settings, postType, postId, renderingMode ) { keepCaretInsideBlock, mediaUpload: hasUploadPermissions ? mediaUpload : undefined, __experimentalBlockPatterns: blockPatterns, - [ unlock( privateApis ).selectBlockPatternsKey ]: ( select ) => - unlock( select( coreStore ) ).getBlockPatternsForPostType( - postType - ), + [ selectBlockPatternsKey ]: ( select ) => { + const { hasFinishedResolution, getBlockPatternsForPostType } = + unlock( select( coreStore ) ); + const patterns = getBlockPatternsForPostType( postType ); + return hasFinishedResolution( 'getBlockPatterns' ) + ? patterns + : undefined; + }, [ unlock( privateApis ).reusableBlocksSelectKey ]: __experimentalReusableBlocksSelect, __experimentalBlockPatternCategories: blockPatternCategories, @@ -327,6 +339,9 @@ function useBlockEditorSettings( settings, postType, postId, renderingMode ) { postType, setIsInserterOpened, sectionRootClientId, + globalStylesData, + globalStylesDataKey, + selectBlockPatternsKey, ] ); } diff --git a/packages/editor/src/components/sidebar/constants.js b/packages/editor/src/components/sidebar/constants.js new file mode 100644 index 00000000000000..be660c21692390 --- /dev/null +++ b/packages/editor/src/components/sidebar/constants.js @@ -0,0 +1,4 @@ +export const sidebars = { + document: 'edit-post/document', + block: 'edit-post/block', +}; diff --git a/packages/edit-post/src/components/sidebar/settings-header/index.js b/packages/editor/src/components/sidebar/header.js similarity index 82% rename from packages/edit-post/src/components/sidebar/settings-header/index.js rename to packages/editor/src/components/sidebar/header.js index 244e21b1acd432..fc4d44ba9e2958 100644 --- a/packages/edit-post/src/components/sidebar/settings-header/index.js +++ b/packages/editor/src/components/sidebar/header.js @@ -5,17 +5,17 @@ import { privateApis as componentsPrivateApis } from '@wordpress/components'; import { __, _x } from '@wordpress/i18n'; import { useSelect } from '@wordpress/data'; import { forwardRef } from '@wordpress/element'; -import { store as editorStore } from '@wordpress/editor'; /** * Internal dependencies */ -import { unlock } from '../../../lock-unlock'; -import { sidebars } from '../settings-sidebar'; +import { store as editorStore } from '../../store'; +import { unlock } from '../../lock-unlock'; +import { sidebars } from './constants'; const { Tabs } = unlock( componentsPrivateApis ); -const SettingsHeader = ( _, ref ) => { +const SidebarHeader = ( _, ref ) => { const { documentLabel } = useSelect( ( select ) => { const { getPostTypeLabel } = select( editorStore ); @@ -46,4 +46,4 @@ const SettingsHeader = ( _, ref ) => { ); }; -export default forwardRef( SettingsHeader ); +export default forwardRef( SidebarHeader ); diff --git a/packages/edit-post/src/components/sidebar/settings-sidebar/index.js b/packages/editor/src/components/sidebar/index.js similarity index 80% rename from packages/edit-post/src/components/sidebar/settings-sidebar/index.js rename to packages/editor/src/components/sidebar/index.js index fd5b136ba461d6..4aa4d03569e116 100644 --- a/packages/edit-post/src/components/sidebar/settings-sidebar/index.js +++ b/packages/editor/src/components/sidebar/index.js @@ -16,45 +16,43 @@ import { import { isRTL, __, sprintf } from '@wordpress/i18n'; import { drawerLeft, drawerRight } from '@wordpress/icons'; import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; -import { - store as editorStore, - PageAttributesPanel, - PluginDocumentSettingPanel, - PluginSidebar, - PostDiscussionPanel, - PostLastRevisionPanel, - PostTaxonomiesPanel, - privateApis as editorPrivateApis, -} from '@wordpress/editor'; import { addQueryArgs } from '@wordpress/url'; import { store as noticesStore } from '@wordpress/notices'; +import { privateApis as componentsPrivateApis } from '@wordpress/components'; +import { store as interfaceStore } from '@wordpress/interface'; /** * Internal dependencies */ -import SettingsHeader from '../settings-header'; -import PostStatus from '../post-status'; -import MetaBoxes from '../../meta-boxes'; -import { store as editPostStore } from '../../../store'; -import { privateApis as componentsPrivateApis } from '@wordpress/components'; -import { unlock } from '../../../lock-unlock'; +import PageAttributesPanel from '../page-attributes/panel'; +import PatternOverridesPanel from '../pattern-overrides-panel'; +import PluginDocumentSettingPanel from '../plugin-document-setting-panel'; +import PluginSidebar from '../plugin-sidebar'; +import PostActions from '../post-actions'; +import PostCardPanel from '../post-card-panel'; +import PostDiscussionPanel from '../post-discussion/panel'; +import PostLastRevisionPanel from '../post-last-revision/panel'; +import PostSummary from './post-summary'; +import PostTaxonomiesPanel from '../post-taxonomies/panel'; +import SidebarHeader from './header'; +import useAutoSwitchEditorSidebars from '../provider/use-auto-switch-editor-sidebars'; +import { sidebars } from './constants'; +import { unlock } from '../../lock-unlock'; +import { store as editorStore } from '../../store'; -const { PostCardPanel, PostActions, interfaceStore } = - unlock( editorPrivateApis ); const { Tabs } = unlock( componentsPrivateApis ); -const { PatternOverridesPanel, useAutoSwitchEditorSidebars } = - unlock( editorPrivateApis ); const SIDEBAR_ACTIVE_BY_DEFAULT = Platform.select( { web: true, native: false, } ); -export const sidebars = { - document: 'edit-post/document', - block: 'edit-post/block', -}; -const SidebarContent = ( { tabName, keyboardShortcut, isEditingTemplate } ) => { +const SidebarContent = ( { + tabName, + keyboardShortcut, + isEditingTemplate, + extraPanels, +} ) => { const tabListRef = useRef( null ); // Because `PluginSidebar` renders a `ComplementaryArea`, we // need to forward the `Tabs` context so it can be passed through the @@ -143,15 +141,15 @@ const SidebarContent = ( { tabName, keyboardShortcut, isEditingTemplate } ) => { identifier={ tabName } header={ - + } closeLabel={ __( 'Close Settings' ) } // This classname is added so we can apply a corrective negative // margin to the panel. // see https://github.com/WordPress/gutenberg/pull/55360#pullrequestreview-1737671049 - className="edit-post-sidebar__panel" - headerClassName="edit-post-sidebar__panel-tabs" + className="editor-sidebar__panel" + headerClassName="editor-sidebar__panel-tabs" /* translators: button label text should, if possible, be under 16 characters. */ title={ __( 'Settings' ) } toggleShortcut={ keyboardShortcut } @@ -167,14 +165,14 @@ const SidebarContent = ( { tabName, keyboardShortcut, isEditingTemplate } ) => { /> } /> - { ! isEditingTemplate && } + { ! isEditingTemplate && } - { ! isEditingTemplate && } + { extraPanels } @@ -184,7 +182,7 @@ const SidebarContent = ( { tabName, keyboardShortcut, isEditingTemplate } ) => { ); }; -const SettingsSidebar = () => { +const Sidebar = ( { extraPanels } ) => { useAutoSwitchEditorSidebars(); const { tabName, keyboardShortcut, isEditingTemplate } = useSelect( ( select ) => { @@ -218,15 +216,15 @@ const SettingsSidebar = () => { [] ); - const { openGeneralSidebar } = useDispatch( editPostStore ); + const { enableComplementaryArea } = useDispatch( interfaceStore ); const onTabSelect = useCallback( ( newSelectedTabId ) => { if ( !! newSelectedTabId ) { - openGeneralSidebar( newSelectedTabId ); + enableComplementaryArea( 'core', newSelectedTabId ); } }, - [ openGeneralSidebar ] + [ enableComplementaryArea ] ); return ( @@ -239,9 +237,10 @@ const SettingsSidebar = () => { tabName={ tabName } keyboardShortcut={ keyboardShortcut } isEditingTemplate={ isEditingTemplate } + extraPanels={ extraPanels } /> ); }; -export default SettingsSidebar; +export default Sidebar; diff --git a/packages/edit-post/src/components/sidebar/post-status/index.js b/packages/editor/src/components/sidebar/post-summary.js similarity index 69% rename from packages/edit-post/src/components/sidebar/post-status/index.js rename to packages/editor/src/components/sidebar/post-summary.js index 99c202463162da..3ab5d5bab5c6ca 100644 --- a/packages/edit-post/src/components/sidebar/post-status/index.js +++ b/packages/editor/src/components/sidebar/post-summary.js @@ -8,40 +8,33 @@ import { PanelBody, } from '@wordpress/components'; import { useDispatch, useSelect } from '@wordpress/data'; -import { - PluginPostStatusInfo, - PostAuthorPanel, - PostSchedulePanel, - PostSyncStatus, - PostURLPanel, - PostTemplatePanel, - PostFeaturedImagePanel, - store as editorStore, - privateApis as editorPrivateApis, -} from '@wordpress/editor'; /** * Internal dependencies */ -import PostTrash from '../post-trash'; -import PostSticky from '../post-sticky'; -import PostSlug from '../post-slug'; -import PostFormat from '../post-format'; -import { unlock } from '../../../lock-unlock'; - -const { - PostStatus: PostStatusPanel, - PrivatePostExcerptPanel, - PostContentInformation, - PostLastEditedPanel, -} = unlock( editorPrivateApis ); +import PluginPostStatusInfo from '../plugin-post-status-info'; +import PostAuthorPanel from '../post-author/panel'; +import PostContentInformation from '../post-content-information'; +import { PrivatePostExcerptPanel as PostExcerptPanel } from '../post-excerpt/panel'; +import PostFeaturedImagePanel from '../post-featured-image/panel'; +import PostFormatPanel from '../post-format/panel'; +import PostLastEditedPanel from '../post-last-edited-panel'; +import PostSchedulePanel from '../post-schedule/panel'; +import PostSlugPanel from '../post-slug/panel'; +import PostStatusPanel from '../post-status'; +import PostStickyPanel from '../post-sticky'; +import PostSyncStatus from '../post-sync-status'; +import PostTemplatePanel from '../post-template/panel'; +import PostTrashPanel from '../post-trash/panel'; +import PostURLPanel from '../post-url/panel'; +import { store as editorStore } from '../../store'; /** * Module Constants */ const PANEL_NAME = 'post-status'; -export default function PostStatus() { +export default function PostSummary() { const { isOpened, isRemoved, showPostContentPanels } = useSelect( ( select ) => { // We use isEditorPanelRemoved to hide the panel if it was programatically removed. We do @@ -74,7 +67,6 @@ export default function PostStatus() { return ( toggleEditorPanelOpened( PANEL_NAME ) } @@ -91,7 +83,7 @@ export default function PostStatus() { - + @@ -108,9 +100,9 @@ export default function PostStatus() { - - - + + + { fills } - + ) } diff --git a/packages/edit-post/src/components/sidebar/style.scss b/packages/editor/src/components/sidebar/style.scss similarity index 64% rename from packages/edit-post/src/components/sidebar/style.scss rename to packages/editor/src/components/sidebar/style.scss index 1921c5cfd7b312..000f4c6123766c 100644 --- a/packages/edit-post/src/components/sidebar/style.scss +++ b/packages/editor/src/components/sidebar/style.scss @@ -1,4 +1,4 @@ -.components-panel__header.edit-post-sidebar__panel-tabs { +.components-panel__header.editor-sidebar__panel-tabs { padding-left: 0; padding-right: $grid-unit-20; @@ -12,7 +12,3 @@ } } } - -.edit-post-sidebar__panel { - margin-top: -1px; -} diff --git a/packages/editor/src/private-apis.js b/packages/editor/src/private-apis.js index 762c6bec89c883..41414743f9a6ba 100644 --- a/packages/editor/src/private-apis.js +++ b/packages/editor/src/private-apis.js @@ -6,22 +6,18 @@ import * as interfaceApis from '@wordpress/interface'; /** * Internal dependencies */ -import CollapsableBlockToolbar from './components/collapsible-block-toolbar'; import EditorCanvas from './components/editor-canvas'; import { ExperimentalEditorProvider } from './components/provider'; import { lock } from './lock-unlock'; import { EntitiesSavedStatesExtensible } from './components/entities-saved-states'; import useAutoSwitchEditorSidebars from './components/provider/use-auto-switch-editor-sidebars'; import useBlockEditorSettings from './components/provider/use-block-editor-settings'; -import DocumentTools from './components/document-tools'; +import Header from './components/header'; import InserterSidebar from './components/inserter-sidebar'; import ListViewSidebar from './components/list-view-sidebar'; -import MoreMenu from './components/more-menu'; import PatternOverridesPanel from './components/pattern-overrides-panel'; import PluginPostExcerpt from './components/post-excerpt/plugin'; import PostPanelRow from './components/post-panel-row'; -import PostViewLink from './components/post-view-link'; -import PreviewDropdown from './components/preview-dropdown'; import PreferencesModal from './components/preferences-modal'; import PostActions from './components/post-actions'; import { usePostActions } from './components/post-actions/actions'; @@ -30,29 +26,31 @@ import PostStatus from './components/post-status'; import ToolsMoreMenuGroup from './components/more-menu/tools-more-menu-group'; import ViewMoreMenuGroup from './components/more-menu/view-more-menu-group'; import { PrivatePostExcerptPanel } from './components/post-excerpt/panel'; -import PostPublishButtonOrToggle from './components/post-publish-button/post-publish-button-or-toggle'; import SavePublishPanels from './components/save-publish-panels'; import PostContentInformation from './components/post-content-information'; import PostLastEditedPanel from './components/post-last-edited-panel'; +import Sidebar from './components/sidebar'; +import { + mergeBaseAndUserConfigs, + GlobalStylesProvider, +} from './components/global-styles-provider'; const { store: interfaceStore, ...remainingInterfaceApis } = interfaceApis; export const privateApis = {}; lock( privateApis, { - CollapsableBlockToolbar, - DocumentTools, EditorCanvas, ExperimentalEditorProvider, EntitiesSavedStatesExtensible, + GlobalStylesProvider, + Header, InserterSidebar, ListViewSidebar, - MoreMenu, + mergeBaseAndUserConfigs, PatternOverridesPanel, PluginPostExcerpt, PostActions, PostPanelRow, - PostViewLink, - PreviewDropdown, PreferencesModal, usePostActions, PostCardPanel, @@ -60,10 +58,10 @@ lock( privateApis, { ToolsMoreMenuGroup, ViewMoreMenuGroup, PrivatePostExcerptPanel, - PostPublishButtonOrToggle, SavePublishPanels, PostContentInformation, PostLastEditedPanel, + Sidebar, // This is a temporary private API while we're updating the site editor to use EditorProvider. useAutoSwitchEditorSidebars, diff --git a/packages/editor/src/private-apis.native.js b/packages/editor/src/private-apis.native.js new file mode 100644 index 00000000000000..78ef82c327f8fd --- /dev/null +++ b/packages/editor/src/private-apis.native.js @@ -0,0 +1,61 @@ +/** + * WordPress dependencies + */ +import * as interfaceApis from '@wordpress/interface'; + +/** + * Internal dependencies + */ +import EditorCanvas from './components/editor-canvas'; +import { ExperimentalEditorProvider } from './components/provider'; +import { lock } from './lock-unlock'; +import { EntitiesSavedStatesExtensible } from './components/entities-saved-states'; +import useAutoSwitchEditorSidebars from './components/provider/use-auto-switch-editor-sidebars'; +import useBlockEditorSettings from './components/provider/use-block-editor-settings'; +import InserterSidebar from './components/inserter-sidebar'; +import ListViewSidebar from './components/list-view-sidebar'; +import PatternOverridesPanel from './components/pattern-overrides-panel'; +import PluginPostExcerpt from './components/post-excerpt/plugin'; +import PostPanelRow from './components/post-panel-row'; +import PreferencesModal from './components/preferences-modal'; +import PostActions from './components/post-actions'; +import { usePostActions } from './components/post-actions/actions'; +import PostCardPanel from './components/post-card-panel'; +import PostStatus from './components/post-status'; +import ToolsMoreMenuGroup from './components/more-menu/tools-more-menu-group'; +import ViewMoreMenuGroup from './components/more-menu/view-more-menu-group'; +import { PrivatePostExcerptPanel } from './components/post-excerpt/panel'; +import SavePublishPanels from './components/save-publish-panels'; +import PostContentInformation from './components/post-content-information'; +import PostLastEditedPanel from './components/post-last-edited-panel'; + +const { store: interfaceStore, ...remainingInterfaceApis } = interfaceApis; + +export const privateApis = {}; +lock( privateApis, { + EditorCanvas, + ExperimentalEditorProvider, + EntitiesSavedStatesExtensible, + InserterSidebar, + ListViewSidebar, + PatternOverridesPanel, + PluginPostExcerpt, + PostActions, + PostPanelRow, + PreferencesModal, + usePostActions, + PostCardPanel, + PostStatus, + ToolsMoreMenuGroup, + ViewMoreMenuGroup, + PrivatePostExcerptPanel, + SavePublishPanels, + PostContentInformation, + PostLastEditedPanel, + + // This is a temporary private API while we're updating the site editor to use EditorProvider. + useAutoSwitchEditorSidebars, + useBlockEditorSettings, + interfaceStore, + ...remainingInterfaceApis, +} ); diff --git a/packages/editor/src/store/private-selectors.js b/packages/editor/src/store/private-selectors.js index 5abd72f13713b0..aa2af9172ff18f 100644 --- a/packages/editor/src/store/private-selectors.js +++ b/packages/editor/src/store/private-selectors.js @@ -75,6 +75,9 @@ export const getInsertionPoint = createRegistrySelector( ( select ) => export function getListViewToggleRef( state ) { return state.listViewToggleRef; } +export function getInserterSidebarToggleRef( state ) { + return state.inserterSidebarToggleRef; +} const CARD_ICONS = { wp_block: symbol, wp_navigation: navigation, diff --git a/packages/editor/src/store/reducer.js b/packages/editor/src/store/reducer.js index 202baa1e7e5cb1..f9b4e05ffa8e5e 100644 --- a/packages/editor/src/store/reducer.js +++ b/packages/editor/src/store/reducer.js @@ -360,6 +360,17 @@ export function listViewToggleRef( state = { current: null } ) { return state; } +/** + * This reducer does nothing aside initializing a ref to the inserter sidebar toggle. + * We will have a unique ref per "editor" instance. + * + * @param {Object} state + * @return {Object} Reference to the inserter sidebar toggle button. + */ +export function inserterSidebarToggleRef( state = { current: null } ) { + return state; +} + export function publishSidebarActive( state = false, action ) { switch ( action.type ) { case 'OPEN_PUBLISH_SIDEBAR': @@ -387,6 +398,7 @@ export default combineReducers( { deviceType, removedPanels, blockInserterPanel, + inserterSidebarToggleRef, listViewPanel, listViewToggleRef, publishSidebarActive, diff --git a/packages/editor/src/style.scss b/packages/editor/src/style.scss index ed61638ffb7ef6..dcc693c08b80a7 100644 --- a/packages/editor/src/style.scss +++ b/packages/editor/src/style.scss @@ -3,12 +3,14 @@ @import "./components/autocompleters/style.scss"; @import "./components/block-manager/style.scss"; @import "./components/collapsible-block-toolbar/style.scss"; +@import "./components/block-settings-menu/style.scss"; @import "./components/document-bar/style.scss"; @import "./components/document-outline/style.scss"; @import "./components/document-tools/style.scss"; @import "./components/editor-notices/style.scss"; @import "./components/entities-saved-states/style.scss"; @import "./components/error-boundary/style.scss"; +@import "./components/header/style.scss"; @import "./components/inserter-sidebar/style.scss"; @import "./components/keyboard-shortcut-help-modal/style.scss"; @import "./components/list-view-sidebar/style.scss"; @@ -26,6 +28,7 @@ @import "./components/post-publish-panel/style.scss"; @import "./components/post-saved-state/style.scss"; @import "./components/post-schedule/style.scss"; +@import "./components/post-slug/style.scss"; @import "./components/post-status/style.scss"; @import "./components/post-sync-status/style.scss"; @import "./components/post-taxonomies/style.scss"; @@ -38,5 +41,6 @@ @import "./components/preview-dropdown/style.scss"; @import "./components/save-publish-panels/style.scss"; @import "./components/start-page-options/style.scss"; +@import "./components/sidebar/style.scss"; @import "./components/table-of-contents/style.scss"; @import "./components/template-areas/style.scss"; diff --git a/packages/element/package.json b/packages/element/package.json index 153e817837ce40..18abd14b0e0a54 100644 --- a/packages/element/package.json +++ b/packages/element/package.json @@ -29,8 +29,8 @@ "sideEffects": false, "dependencies": { "@babel/runtime": "^7.16.0", - "@types/react": "^18.0.21", - "@types/react-dom": "^18.0.6", + "@types/react": "^18.2.79", + "@types/react-dom": "^18.2.25", "@wordpress/escape-html": "file:../escape-html", "change-case": "^4.1.2", "is-plain-object": "^5.0.0", diff --git a/packages/interactivity/CHANGELOG.md b/packages/interactivity/CHANGELOG.md index fa4fee5976b0cd..56352eb4c489bc 100644 --- a/packages/interactivity/CHANGELOG.md +++ b/packages/interactivity/CHANGELOG.md @@ -6,6 +6,8 @@ - Allow multiple event handlers for the same type with `data-wp-on-document` and `data-wp-on-window`. ([#61009](https://github.com/WordPress/gutenberg/pull/61009)) +- Prevent wrong written directives from killing the runtime ([#61249](https://github.com/WordPress/gutenberg/pull/61249)) + ## 5.6.0 (2024-05-02) ## 5.5.0 (2024-04-19) diff --git a/packages/interactivity/src/vdom.ts b/packages/interactivity/src/vdom.ts index 05b49012c48861..9e6221bb871f4f 100644 --- a/packages/interactivity/src/vdom.ts +++ b/packages/interactivity/src/vdom.ts @@ -118,11 +118,23 @@ export function toVdom( root ) { if ( directives.length ) { props.__directives = directives.reduce( ( obj, [ name, ns, value ] ) => { - const [ , prefix, suffix = 'default' ] = - directiveParser.exec( name ); - if ( ! obj[ prefix ] ) { - obj[ prefix ] = []; + const directiveMatch = directiveParser.exec( name ); + if ( directiveMatch === null ) { + if ( + // @ts-expect-error This is a debug-only warning. + typeof SCRIPT_DEBUG !== 'undefined' && + // @ts-expect-error This is a debug-only warning. + SCRIPT_DEBUG === true + ) { + // eslint-disable-next-line no-console + console.warn( `Invalid directive: ${ name }.` ); + } + return obj; } + const prefix = directiveMatch[ 1 ] || ''; + const suffix = directiveMatch[ 2 ] || 'default'; + + obj[ prefix ] = obj[ prefix ] || []; obj[ prefix ].push( { namespace: ns ?? currentNamespace(), value, diff --git a/packages/interface/src/components/complementary-area-header/style.scss b/packages/interface/src/components/complementary-area-header/style.scss index 20fbe881d5694c..a6b9b532069602 100644 --- a/packages/interface/src/components/complementary-area-header/style.scss +++ b/packages/interface/src/components/complementary-area-header/style.scss @@ -17,7 +17,8 @@ .interface-complementary-area-header { background: $white; - padding-right: $grid-unit-05; + padding-right: $grid-unit-15; // Reduced padding to account for close buttons. + gap: $grid-unit-10; // Always ensure space between contents and close buttons. .interface-complementary-area-header__title { margin: 0; @@ -36,11 +37,3 @@ } } } - -// This overrides the negative margins between two consecutives panels. -// since the first panel is hidden. -.components-panel__header + .interface-complementary-area-header { - @include break-medium() { - margin-top: 0; - } -} diff --git a/packages/interface/src/components/complementary-area/index.js b/packages/interface/src/components/complementary-area/index.js index ca80ae75e2e2f1..b37b8b7da33d02 100644 --- a/packages/interface/src/components/complementary-area/index.js +++ b/packages/interface/src/components/complementary-area/index.js @@ -302,6 +302,7 @@ function ComplementaryArea( { smallScreenTitle={ smallScreenTitle } toggleButtonProps={ { label: closeLabel, + size: 'small', shortcut: toggleShortcut, scope, identifier, @@ -329,6 +330,7 @@ function ComplementaryArea( { } isPressed={ isPinned } aria-expanded={ isPinned } + size="compact" /> ) } diff --git a/packages/interface/src/components/complementary-area/style.scss b/packages/interface/src/components/complementary-area/style.scss index 143911c43ecc53..c15be5678a4460 100644 --- a/packages/interface/src/components/complementary-area/style.scss +++ b/packages/interface/src/components/complementary-area/style.scss @@ -24,7 +24,7 @@ top: 0; z-index: z-index(".interface-complementary-area .components-panel__header"); - &.edit-post-sidebar__panel-tabs { + &.editor-sidebar__panel-tabs { top: $panel-header-height; @include break-medium() { @@ -39,6 +39,7 @@ h2 { font-size: $default-font-size; + font-weight: 500; color: $gray-900; margin-bottom: 1.5em; } diff --git a/packages/interface/src/components/interface-skeleton/style.scss b/packages/interface/src/components/interface-skeleton/style.scss index a140b73be55b94..a0e56658355ac8 100644 --- a/packages/interface/src/components/interface-skeleton/style.scss +++ b/packages/interface/src/components/interface-skeleton/style.scss @@ -5,8 +5,10 @@ html.interface-interface-skeleton__html-container { width: 100%; @include break-medium() { - position: initial; - width: initial; + &:not(:has(.is-zoom-out)) { + position: initial; + width: initial; + } } } diff --git a/packages/patterns/src/components/patterns-manage-button.js b/packages/patterns/src/components/patterns-manage-button.js index f2bd798a7b6fad..bab9cab11462ab 100644 --- a/packages/patterns/src/components/patterns-manage-button.js +++ b/packages/patterns/src/components/patterns-manage-button.js @@ -37,7 +37,7 @@ function PatternsManageButton( { clientId } ) { // The site editor and templates both check whether the user // has edit_theme_options capabilities. We can leverage that here // and omit the manage patterns link if the user can't access it. - managePatternsUrl: canUser( 'read', 'templates' ) + managePatternsUrl: canUser( 'create', 'templates' ) ? addQueryArgs( 'site-editor.php', { path: '/patterns', } ) diff --git a/packages/react-native-bridge/common/gutenberg-web-single-block/editor-style-overrides.css b/packages/react-native-bridge/common/gutenberg-web-single-block/editor-style-overrides.css index ce39beede024fa..484cdfebfbd9bc 100644 --- a/packages/react-native-bridge/common/gutenberg-web-single-block/editor-style-overrides.css +++ b/packages/react-native-bridge/common/gutenberg-web-single-block/editor-style-overrides.css @@ -64,17 +64,17 @@ } /* Remove tabs from sidebar panel, leaving the \'x\' button */ -.edit-post-sidebar__panel-tabs { +.editor-sidebar__panel-tabs { display: none; } /* Remove \'(no-title)\' string from sidebar header */ -.edit-post-sidebar-header__title { +.editor-sidebar-header__title { display: none; } /* Move \'x\' close button to the end on sidebar header */ -.edit-post-sidebar-header__small { +.editor-sidebar-header__small { justify-content: flex-end; } diff --git a/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-blocks-manage-button.js b/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-blocks-manage-button.js index a535eade291a09..c0138517400fb2 100644 --- a/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-blocks-manage-button.js +++ b/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-blocks-manage-button.js @@ -36,7 +36,7 @@ function ReusableBlocksManageButton( { clientId } ) { // The site editor and templates both check whether the user // has edit_theme_options capabilities. We can leverage that here // and omit the manage patterns link if the user can't access it. - managePatternsUrl: canUser( 'read', 'templates' ) + managePatternsUrl: canUser( 'create', 'templates' ) ? addQueryArgs( 'site-editor.php', { path: '/patterns', } ) diff --git a/packages/router/src/history.js b/packages/router/src/history.js index df0e2b219cfcef..56c85914a5453f 100644 --- a/packages/router/src/history.js +++ b/packages/router/src/history.js @@ -13,13 +13,28 @@ const history = createBrowserHistory(); const originalHistoryPush = history.push; const originalHistoryReplace = history.replace; +// Preserve the `wp_theme_preview` query parameter when navigating +// around the Site Editor. +// TODO: move this hack out of the router into Site Editor code. +function preserveThemePreview( params ) { + if ( params.hasOwnProperty( 'wp_theme_preview' ) ) { + return params; + } + const currentSearch = new URLSearchParams( history.location.search ); + const currentThemePreview = currentSearch.get( 'wp_theme_preview' ); + if ( currentThemePreview === null ) { + return params; + } + return { ...params, wp_theme_preview: currentThemePreview }; +} + function push( params, state ) { - const search = buildQueryString( params ); + const search = buildQueryString( preserveThemePreview( params ) ); return originalHistoryPush.call( history, { search }, state ); } function replace( params, state ) { - const search = buildQueryString( params ); + const search = buildQueryString( preserveThemePreview( params ) ); return originalHistoryReplace.call( history, { search }, state ); } diff --git a/packages/scripts/CHANGELOG.md b/packages/scripts/CHANGELOG.md index 36c3450f4e8c63..d2f8b72390ad9e 100644 --- a/packages/scripts/CHANGELOG.md +++ b/packages/scripts/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### New Features + +- Add RTL support when building CSS styles with `build` and `start` scripts ([#61540](https://github.com/WordPress/gutenberg/pull/61540)). + ## 27.8.0 (2024-05-02) ## 27.7.0 (2024-04-19) diff --git a/packages/scripts/config/webpack.config.js b/packages/scripts/config/webpack.config.js index 386cc1be49d401..b7f61eb215eaf0 100644 --- a/packages/scripts/config/webpack.config.js +++ b/packages/scripts/config/webpack.config.js @@ -9,6 +9,7 @@ const browserslist = require( 'browserslist' ); const MiniCSSExtractPlugin = require( 'mini-css-extract-plugin' ); const { basename, dirname, resolve } = require( 'path' ); const ReactRefreshWebpackPlugin = require( '@pmmmwh/react-refresh-webpack-plugin' ); +const RtlCssPlugin = require( 'rtlcss-webpack-plugin' ); const TerserPlugin = require( 'terser-webpack-plugin' ); const { realpathSync } = require( 'fs' ); const { sync: glob } = require( 'fast-glob' ); @@ -382,6 +383,10 @@ const scriptConfig = { process.env.WP_BUNDLE_ANALYZER && new BundleAnalyzerPlugin(), // MiniCSSExtractPlugin to extract the CSS thats gets imported into JavaScript. new MiniCSSExtractPlugin( { filename: '[name].css' } ), + // RtlCssPlugin to generate RTL CSS files. + new RtlCssPlugin( { + filename: `[name]-rtl.css`, + } ), // React Fast Refresh. hasReactFastRefresh && new ReactRefreshWebpackPlugin(), // WP_NO_EXTERNALS global variable controls whether scripts' assets get diff --git a/packages/scripts/package.json b/packages/scripts/package.json index 4a3f319525a726..d5cdd85940dd48 100644 --- a/packages/scripts/package.json +++ b/packages/scripts/package.json @@ -79,6 +79,7 @@ "react-refresh": "^0.14.0", "read-pkg-up": "^7.0.1", "resolve-bin": "^0.4.0", + "rtlcss-webpack-plugin": "^4.0.7", "sass": "^1.35.2", "sass-loader": "^12.1.0", "source-map-loader": "^3.0.0", diff --git a/packages/style-engine/src/styles/background/index.ts b/packages/style-engine/src/styles/background/index.ts index a8c8679888e153..6e79636cfda124 100644 --- a/packages/style-engine/src/styles/background/index.ts +++ b/packages/style-engine/src/styles/background/index.ts @@ -39,7 +39,7 @@ const backgroundImage = { }; const backgroundPosition = { - name: 'backgroundRepeat', + name: 'backgroundPosition', generate: ( style: Style, options: StyleOptions ) => { return generateRule( style, diff --git a/post-content.php b/post-content.php index 0bdeb30733f6cd..3c9b18087b0e84 100644 --- a/post-content.php +++ b/post-content.php @@ -61,7 +61,7 @@ -