From 4df46d76e5136c0ab9dbea10c8dd25a418c638b9 Mon Sep 17 00:00:00 2001 From: Robert Anderson Date: Mon, 26 Jun 2023 16:18:19 +1000 Subject: [PATCH 001/266] Page Content Focus: Switch to Page panel when deselecting a block (#51881) --- .../edit-site/src/components/sidebar-edit-mode/index.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/edit-site/src/components/sidebar-edit-mode/index.js b/packages/edit-site/src/components/sidebar-edit-mode/index.js index 80a02c11bd88f4..8a36a0b5395610 100644 --- a/packages/edit-site/src/components/sidebar-edit-mode/index.js +++ b/packages/edit-site/src/components/sidebar-edit-mode/index.js @@ -56,11 +56,13 @@ export function SidebarComplementaryAreaFills() { useEffect( () => { // Don't automatically switch tab when the sidebar is closed or when we // are focused on page content. - if ( ! isEditorSidebarOpened || hasPageContentFocus ) { + if ( ! isEditorSidebarOpened ) { return; } if ( hasBlockSelection ) { - enableComplementaryArea( STORE_NAME, SIDEBAR_BLOCK ); + if ( ! hasPageContentFocus ) { + enableComplementaryArea( STORE_NAME, SIDEBAR_BLOCK ); + } } else { enableComplementaryArea( STORE_NAME, SIDEBAR_TEMPLATE ); } From 74f3ce32ec7cc71633a326c49156fc55d418a7e9 Mon Sep 17 00:00:00 2001 From: Kai Hao Date: Mon, 26 Jun 2023 14:42:57 +0800 Subject: [PATCH 002/266] Fix flakiness of saving entities in the site editor (#51728) --- .../e2e-test-utils-playwright/src/editor/site-editor.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/e2e-test-utils-playwright/src/editor/site-editor.ts b/packages/e2e-test-utils-playwright/src/editor/site-editor.ts index 432e8c15b120a7..b81f9983e2813e 100644 --- a/packages/e2e-test-utils-playwright/src/editor/site-editor.ts +++ b/packages/e2e-test-utils-playwright/src/editor/site-editor.ts @@ -24,10 +24,8 @@ export async function saveSiteEditorEntities( this: Editor ) { .getByRole( 'button', { name: 'Save', exact: true } ) .click(); - // A role selector cannot be used here because it needs to check that the `is-busy` class is not present. await this.page - .locator( '[aria-label="Editor top bar"] [aria-label="Saved"].is-busy' ) - .waitFor( { - state: 'hidden', - } ); + .getByRole( 'button', { name: 'Dismiss this notice' } ) + .getByText( 'Site updated.' ) + .waitFor(); } From 67b5ebed8236fa4100be04d2ef3bbf632f49ae42 Mon Sep 17 00:00:00 2001 From: Nik Tsekouras Date: Mon, 26 Jun 2023 11:22:38 +0300 Subject: [PATCH 003/266] Add `manage all custom patterns` command (#51845) * Add manage all custom patterns command * reorganise with useAdminNavigationCommands --- ...type-commands.js => admin-navigation-commands.js} | 12 ++++++++++-- packages/core-commands/src/private-apis.js | 4 ++-- 2 files changed, 12 insertions(+), 4 deletions(-) rename packages/core-commands/src/{add-post-type-commands.js => admin-navigation-commands.js} (60%) diff --git a/packages/core-commands/src/add-post-type-commands.js b/packages/core-commands/src/admin-navigation-commands.js similarity index 60% rename from packages/core-commands/src/add-post-type-commands.js rename to packages/core-commands/src/admin-navigation-commands.js index 47e6014f569444..577e7258df0b63 100644 --- a/packages/core-commands/src/add-post-type-commands.js +++ b/packages/core-commands/src/admin-navigation-commands.js @@ -3,9 +3,9 @@ */ import { useCommand } from '@wordpress/commands'; import { __ } from '@wordpress/i18n'; -import { plus } from '@wordpress/icons'; +import { external, plus } from '@wordpress/icons'; -export function useAddPostTypeCommands() { +export function useAdminNavigationCommands() { useCommand( { name: 'core/add-new-post', label: __( 'Add new post' ), @@ -22,4 +22,12 @@ export function useAddPostTypeCommands() { document.location.href = 'post-new.php?post_type=page'; }, } ); + useCommand( { + name: 'core/manage-reusable-blocks', + label: __( 'Manage all custom patterns' ), + callback: () => { + document.location.href = 'edit.php?post_type=wp_block'; + }, + icon: external, + } ); } diff --git a/packages/core-commands/src/private-apis.js b/packages/core-commands/src/private-apis.js index b0e0cd87040f6a..de5b0de197600f 100644 --- a/packages/core-commands/src/private-apis.js +++ b/packages/core-commands/src/private-apis.js @@ -1,12 +1,12 @@ /** * Internal dependencies */ -import { useAddPostTypeCommands } from './add-post-type-commands'; +import { useAdminNavigationCommands } from './admin-navigation-commands'; import { useSiteEditorNavigationCommands } from './site-editor-navigation-commands'; import { lock } from './lock-unlock'; function useCommands() { - useAddPostTypeCommands(); + useAdminNavigationCommands(); useSiteEditorNavigationCommands(); } From e252453acd2f421094bf8a218c56ca7ccdf783bd Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Mon, 26 Jun 2023 17:26:17 +0900 Subject: [PATCH 004/266] Sidebar Navigation: Refactoring the delete modal with ConfirmDialog (#51867) --- .../delete-modal.js | 49 ++++++------------- 1 file changed, 14 insertions(+), 35 deletions(-) diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/delete-modal.js b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/delete-modal.js index ca9bb4e84d61cf..16bf88af41eacc 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/delete-modal.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/delete-modal.js @@ -1,45 +1,24 @@ /** * WordPress dependencies */ -import { - __experimentalHStack as HStack, - __experimentalVStack as VStack, - Button, - Modal, -} from '@wordpress/components'; +import { __experimentalConfirmDialog as ConfirmDialog } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; export default function RenameModal( { onClose, onConfirm } ) { return ( - -
- -

- { __( - 'Are you sure you want to delete this Navigation menu?' - ) } -

- - + { + e.preventDefault(); + onConfirm(); - - -
-
-
+ // Immediate close avoids ability to hit delete multiple times. + onClose(); + } } + onCancel={ onClose } + confirmButtonText={ __( 'Delete' ) } + > + { __( 'Are you sure you want to delete this Navigation menu?' ) } + ); } From 7ba89bc9cc06cdbeac700f985ad58da6980da98e Mon Sep 17 00:00:00 2001 From: James Koster Date: Mon, 26 Jun 2023 09:30:19 +0100 Subject: [PATCH 005/266] Add top margin to page details (#51858) --- .../sidebar-navigation-screen-details-panel/style.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-details-panel/style.scss b/packages/edit-site/src/components/sidebar-navigation-screen-details-panel/style.scss index e94894c0d6ea39..3beef739f78082 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-details-panel/style.scss +++ b/packages/edit-site/src/components/sidebar-navigation-screen-details-panel/style.scss @@ -1,5 +1,5 @@ .edit-site-sidebar-navigation-details-screen-panel { - margin-bottom: $grid-unit-30; + margin: $grid-unit-30 0; &:last-of-type { margin-bottom: 0; From aafef4ff862dea699fa50b5ad13e88f32f1772a1 Mon Sep 17 00:00:00 2001 From: Juan Aldasoro Date: Mon, 26 Jun 2023 10:54:48 +0200 Subject: [PATCH 006/266] Use block label and icon for the inserter draggable chip. (#51048) * Use block label and icon for the inserter draggable blocks. Adjust the draggable chip width to respect the max-content. * Only send `Pattern` as the label when it is a pattern. * Apply suggestions from code review. * Remove empty lines. * add deps --------- Co-authored-by: ntsekouras --- .../src/components/block-draggable/style.scss | 1 + .../components/inserter-draggable-blocks/index.js | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/style.scss b/packages/block-editor/src/components/block-draggable/style.scss index 9f6197efbe0c8a..a27d4c4caf2f29 100644 --- a/packages/block-editor/src/components/block-draggable/style.scss +++ b/packages/block-editor/src/components/block-draggable/style.scss @@ -15,6 +15,7 @@ height: $block-toolbar-height; padding: 0 ( $grid-unit-15 + $border-width ); user-select: none; + width: max-content; svg { fill: currentColor; diff --git a/packages/block-editor/src/components/inserter-draggable-blocks/index.js b/packages/block-editor/src/components/inserter-draggable-blocks/index.js index 0de9b3a2260f6b..42cae8c7bcde98 100644 --- a/packages/block-editor/src/components/inserter-draggable-blocks/index.js +++ b/packages/block-editor/src/components/inserter-draggable-blocks/index.js @@ -2,7 +2,8 @@ * WordPress dependencies */ import { Draggable } from '@wordpress/components'; -import { serialize } from '@wordpress/blocks'; +import { serialize, store as blocksStore } from '@wordpress/blocks'; +import { useSelect } from '@wordpress/data'; /** * Internal dependencies */ @@ -20,6 +21,16 @@ const InserterDraggableBlocks = ( { blocks, }; + const blockTypeIcon = useSelect( + ( select ) => { + const { getBlockType } = select( blocksStore ); + return ( + blocks.length === 1 && getBlockType( blocks[ 0 ].name )?.icon + ); + }, + [ blocks ] + ); + return ( } From a75a63372ce9a0b4e139f9c6db8b2dcafd55eda3 Mon Sep 17 00:00:00 2001 From: Robert Anderson Date: Mon, 26 Jun 2023 19:11:03 +1000 Subject: [PATCH 007/266] Only show Page Content Focus commands when in edit mode (#51888) --- .../hooks/commands/use-edit-mode-commands.js | 91 +++++++++++-------- 1 file changed, 53 insertions(+), 38 deletions(-) diff --git a/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js b/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js index 7d6dcdbd05440d..63e20b9c22c14e 100644 --- a/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js +++ b/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js @@ -30,51 +30,63 @@ import { unlock } from '../../lock-unlock'; const { useHistory } = unlock( routerPrivateApis ); -function useEditModeCommandLoader() { - const { isLoaded, record: template } = useEditedEntityRecord(); - const { removeTemplate, revertTemplate, setHasPageContentFocus } = - useDispatch( editSiteStore ); - const history = useHistory(); - const { isPage, hasPageContentFocus } = useSelect( +function usePageContentFocusCommands() { + const { isPage, canvasMode, hasPageContentFocus } = useSelect( ( select ) => ( { isPage: select( editSiteStore ).isPage(), + canvasMode: unlock( select( editSiteStore ) ).getCanvasMode(), hasPageContentFocus: select( editSiteStore ).hasPageContentFocus(), } ), [] ); + const { setHasPageContentFocus } = useDispatch( editSiteStore ); - if ( ! isLoaded ) { - return { isLoading: true, commands: [] }; + if ( ! isPage || canvasMode !== 'edit' ) { + return { isLoading: false, commands: [] }; } const commands = []; - if ( isPage ) { - if ( hasPageContentFocus ) { - commands.push( { - name: 'core/switch-to-template-focus', - label: __( 'Edit template' ), - icon: layout, - context: 'site-editor-edit', - callback: ( { close } ) => { - setHasPageContentFocus( false ); - close(); - }, - } ); - } else { - commands.push( { - name: 'core/switch-to-page-focus', - label: __( 'Back to page' ), - icon: page, - context: 'site-editor-edit', - callback: ( { close } ) => { - setHasPageContentFocus( true ); - close(); - }, - } ); - } + if ( hasPageContentFocus ) { + commands.push( { + name: 'core/switch-to-template-focus', + label: __( 'Edit template' ), + icon: layout, + callback: ( { close } ) => { + setHasPageContentFocus( false ); + close(); + }, + } ); + } else { + commands.push( { + name: 'core/switch-to-page-focus', + label: __( 'Back to page' ), + icon: page, + callback: ( { close } ) => { + setHasPageContentFocus( true ); + close(); + }, + } ); + } + + return { isLoading: false, commands }; +} + +function useManipulateDocumentCommands() { + const { isLoaded, record: template } = useEditedEntityRecord(); + const { removeTemplate, revertTemplate } = useDispatch( editSiteStore ); + const history = useHistory(); + const hasPageContentFocus = useSelect( + ( select ) => select( editSiteStore ).hasPageContentFocus(), + [] + ); + + if ( ! isLoaded ) { + return { isLoading: true, commands: [] }; } + const commands = []; + if ( isTemplateRevertable( template ) && ! hasPageContentFocus ) { const label = template.type === 'wp_template' @@ -100,7 +112,6 @@ function useEditModeCommandLoader() { name: 'core/remove-template', label, icon: trash, - context: 'site-editor-edit', callback: ( { close } ) => { removeTemplate( template ); // Navigate to the template list @@ -118,13 +129,11 @@ function useEditModeCommandLoader() { }; } -function useEditUICommandLoader() { +function useEditUICommands() { const { openGeneralSidebar, closeGeneralSidebar, switchEditorMode } = useDispatch( editSiteStore ); const { canvasMode, editorMode, activeSidebar } = useSelect( ( select ) => ( { - isPage: select( editSiteStore ).isPage(), - hasPageContentFocus: select( editSiteStore ).hasPageContentFocus(), canvasMode: unlock( select( editSiteStore ) ).getCanvasMode(), editorMode: select( editSiteStore ).getEditorMode(), activeSidebar: select( interfaceStore ).getActiveComplementaryArea( @@ -206,14 +215,20 @@ function useEditUICommandLoader() { } export function useEditModeCommands() { + useCommandLoader( { + name: 'core/edit-site/page-content-focus', + hook: usePageContentFocusCommands, + context: 'site-editor-edit', + } ); + useCommandLoader( { name: 'core/edit-site/manipulate-document', - hook: useEditModeCommandLoader, + hook: useManipulateDocumentCommands, context: 'site-editor-edit', } ); useCommandLoader( { name: 'core/edit-site/edit-ui', - hook: useEditUICommandLoader, + hook: useEditUICommands, } ); } From d767805373382ab77a43bb1fbad4a8db6746c909 Mon Sep 17 00:00:00 2001 From: Marin Atanasov <8436925+tyxla@users.noreply.github.com> Date: Mon, 26 Jun 2023 13:11:58 +0300 Subject: [PATCH 008/266] Edit Site: Make loading spinner colors consistent (#51857) * Edit Site: Make loading spinner colors consistent * Reduce opacity of spinner track --- .../edit-site/src/components/canvas-spinner/index.js | 12 +----------- .../src/components/canvas-spinner/style.scss | 4 ++++ 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/packages/edit-site/src/components/canvas-spinner/index.js b/packages/edit-site/src/components/canvas-spinner/index.js index 047e526e97347c..d5fae75fd7d952 100644 --- a/packages/edit-site/src/components/canvas-spinner/index.js +++ b/packages/edit-site/src/components/canvas-spinner/index.js @@ -2,21 +2,11 @@ * WordPress dependencies */ import { Spinner } from '@wordpress/components'; -import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; - -/** - * Internal dependencies - */ -import { unlock } from '../../lock-unlock'; - -const { useGlobalStyle } = unlock( blockEditorPrivateApis ); export default function CanvasSpinner() { - const [ textColor ] = useGlobalStyle( 'color.text' ); - return (
- +
); } diff --git a/packages/edit-site/src/components/canvas-spinner/style.scss b/packages/edit-site/src/components/canvas-spinner/style.scss index 3178cbaeec58d4..2f0626b80363fe 100644 --- a/packages/edit-site/src/components/canvas-spinner/style.scss +++ b/packages/edit-site/src/components/canvas-spinner/style.scss @@ -4,4 +4,8 @@ display: flex; align-items: center; justify-content: center; + + circle { + stroke: rgba($black, 0.3); + } } From 12a9749e4393ca6f7ca8c17a16f45684524c60f1 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Mon, 26 Jun 2023 12:45:57 +0200 Subject: [PATCH 009/266] ZStack: fix component bounding box to match children (#51836) * ZStack: rewrite using CSS grid * Use first-of-type instead of fist-child * CHANGELOG * Improve comment * Apply styles once in the parent wrapper * Avoid each child view from expanding to all available space * Remove unnecessary wrapeprs in storybook exmaple --- packages/components/CHANGELOG.md | 1 + packages/components/src/z-stack/component.tsx | 6 ++- .../components/src/z-stack/stories/index.tsx | 19 +++----- packages/components/src/z-stack/styles.ts | 47 +++++++++---------- 4 files changed, 34 insertions(+), 39 deletions(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index da0ba4d1b3ff32..01db98978c1088 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -17,6 +17,7 @@ - `Button`: Remove unnecessary margin from dashicon ([#51395](https://github.com/WordPress/gutenberg/pull/51395)). - `Autocomplete`: Announce how many results are available to screen readers when suggestions list first renders ([#51018](https://github.com/WordPress/gutenberg/pull/51018)). - `ConfirmDialog`: Ensure onConfirm isn't called an extra time when submitting one of the buttons using the keyboard ([#51730](https://github.com/WordPress/gutenberg/pull/51730)). +- `ZStack`: ZStack: fix component bounding box to match children ([#51836](https://github.com/WordPress/gutenberg/pull/51836)). ### Internal diff --git a/packages/components/src/z-stack/component.tsx b/packages/components/src/z-stack/component.tsx index 1cbcac56e4fb70..e087f8536e3401 100644 --- a/packages/components/src/z-stack/component.tsx +++ b/packages/components/src/z-stack/component.tsx @@ -35,13 +35,14 @@ function UnconnectedZStack( const clonedChildren = validChildren.map( ( child, index ) => { const zIndex = isReversed ? childrenLastIndex - index : index; - const offsetAmount = offset * index; + // Only when the component is layered, the offset needs to be multiplied by + // the item's index, so that items can correctly stack at the right distance + const offsetAmount = isLayered ? offset * index : offset; const key = isValidElement( child ) ? child.key : index; return ( { clonedChildren } diff --git a/packages/components/src/z-stack/stories/index.tsx b/packages/components/src/z-stack/stories/index.tsx index b04e19962aed15..9b0b0803780339 100644 --- a/packages/components/src/z-stack/stories/index.tsx +++ b/packages/components/src/z-stack/stories/index.tsx @@ -8,7 +8,6 @@ import type { ComponentMeta, ComponentStory } from '@storybook/react'; * Internal dependencies */ import { Elevation } from '../../elevation'; -import { HStack } from '../../h-stack'; import { View } from '../../view'; import { ZStack } from '..'; @@ -55,18 +54,12 @@ const Avatar = ( { const Template: ComponentStory< typeof ZStack > = ( args ) => { return ( - - - - - - - - - - - - + + + + + + ); }; diff --git a/packages/components/src/z-stack/styles.ts b/packages/components/src/z-stack/styles.ts index d0bf20d38b1f4c..186f268e643663 100644 --- a/packages/components/src/z-stack/styles.ts +++ b/packages/components/src/z-stack/styles.ts @@ -4,36 +4,35 @@ import { css } from '@emotion/react'; import styled from '@emotion/styled'; -/** - * Internal dependencies - */ -import { rtl } from '../utils'; - -export const ZStackView = styled.div` - display: flex; - position: relative; -`; - export const ZStackChildView = styled.div< { - isLayered: boolean; offsetAmount: number; zIndex: number; } >` - ${ ( { isLayered, offsetAmount } ) => - isLayered - ? css( rtl( { marginLeft: offsetAmount } )() ) - : css( rtl( { right: offsetAmount * -1 } )() ) } + &:not( :first-of-type ) { + ${ ( { offsetAmount } ) => + css( { + marginInlineStart: offsetAmount, + } ) }; + } - ${ ( { isLayered } ) => - isLayered ? positionAbsolute : positionRelative } - - ${ ( { zIndex } ) => css( { zIndex } ) } -`; - -const positionAbsolute = css` - position: absolute; + ${ ( { zIndex } ) => css( { zIndex } ) }; `; -const positionRelative = css` +export const ZStackView = styled.div< { + isLayered: boolean; +} >` + display: inline-grid; + grid-auto-flow: column; position: relative; + + & > ${ ZStackChildView } { + position: relative; + justify-self: start; + + ${ ( { isLayered } ) => + isLayered + ? // When `isLayered` is true, all items overlap in the same grid cell + css( { gridRowStart: 1, gridColumnStart: 1 } ) + : undefined }; + } `; From 48b30f4b7da156293b4ffd568db4f879eacde852 Mon Sep 17 00:00:00 2001 From: James Koster Date: Mon, 26 Jun 2023 12:19:19 +0100 Subject: [PATCH 010/266] Update colors (#51856) --- packages/edit-site/src/components/layout/style.scss | 2 +- packages/edit-site/src/components/page-library/style.scss | 4 ++-- .../edit-site/src/components/sidebar-button/style.scss | 2 +- .../sidebar-navigation-screen-navigation-menu/style.scss | 4 ++-- .../src/components/sidebar-navigation-screen/index.js | 2 +- .../src/components/sidebar-navigation-screen/style.scss | 1 - packages/edit-site/src/components/site-hub/style.scss | 7 ++++--- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/edit-site/src/components/layout/style.scss b/packages/edit-site/src/components/layout/style.scss index d42a9e5c5ae203..126a31bfe34d23 100644 --- a/packages/edit-site/src/components/layout/style.scss +++ b/packages/edit-site/src/components/layout/style.scss @@ -1,7 +1,7 @@ .edit-site-layout { height: 100%; background: $gray-900; - color: $white; + color: $gray-400; display: flex; flex-direction: column; diff --git a/packages/edit-site/src/components/page-library/style.scss b/packages/edit-site/src/components/page-library/style.scss index e663fcdc248414..f0bb6b30b9db62 100644 --- a/packages/edit-site/src/components/page-library/style.scss +++ b/packages/edit-site/src/components/page-library/style.scss @@ -6,7 +6,7 @@ } .components-heading { - color: $white; + color: $gray-200; } @include break-medium { @@ -78,7 +78,7 @@ .edit-site-library__search { &#{&} input[type="search"] { background: $gray-800; - color: $gray-100; + color: $gray-200; &:focus { background: $gray-800; diff --git a/packages/edit-site/src/components/sidebar-button/style.scss b/packages/edit-site/src/components/sidebar-button/style.scss index 8388aae266e3ed..5135f97869bb8b 100644 --- a/packages/edit-site/src/components/sidebar-button/style.scss +++ b/packages/edit-site/src/components/sidebar-button/style.scss @@ -19,6 +19,6 @@ &:focus, &:not([aria-disabled="true"]):active, &[aria-expanded="true"] { - color: $white; + color: $gray-100; } } diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/style.scss b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/style.scss index e00f9210b4e4bc..ae35cb4525b88b 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/style.scss +++ b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/style.scss @@ -1,10 +1,10 @@ .sidebar-navigation__more-menu { .components-button { - color: $gray-600; + color: $gray-200; &:hover, &:focus, &[aria-current] { - color: $white; + color: $gray-100; } } } diff --git a/packages/edit-site/src/components/sidebar-navigation-screen/index.js b/packages/edit-site/src/components/sidebar-navigation-screen/index.js index 74aeb621f60dd3..4c0d9d82f02cb7 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen/index.js @@ -90,7 +90,7 @@ export default function SidebarNavigationScreen( { ) } diff --git a/packages/edit-site/src/components/sidebar-navigation-screen/style.scss b/packages/edit-site/src/components/sidebar-navigation-screen/style.scss index 57473b58fac480..dadee024b0eadd 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen/style.scss +++ b/packages/edit-site/src/components/sidebar-navigation-screen/style.scss @@ -12,7 +12,6 @@ } .edit-site-sidebar-navigation-screen__content { - color: $gray-400; padding: 0 $grid-unit-20; .components-item-group { diff --git a/packages/edit-site/src/components/site-hub/style.scss b/packages/edit-site/src/components/site-hub/style.scss index cc2254a6231ee3..d0689fec4efa9e 100644 --- a/packages/edit-site/src/components/site-hub/style.scss +++ b/packages/edit-site/src/components/site-hub/style.scss @@ -20,7 +20,7 @@ opacity: 1; } svg { - fill: $white; + fill: $gray-200; } } &:hover { @@ -55,12 +55,13 @@ .edit-site-site-hub__site-title { margin-left: $grid-unit-05; flex-grow: 1; + color: $gray-200; } .edit-site-site-hub_toggle-command-center { - color: $white; + color: $gray-200; &:hover { - color: $white; + color: $gray-100; } } From f33ea53229f815c5a3ddf8b49888c78e595cdac3 Mon Sep 17 00:00:00 2001 From: Artemio Morales Date: Mon, 26 Jun 2023 09:26:29 -0500 Subject: [PATCH 011/266] Image block: Lightbox animation improvements (#51721) * Add logic to use low-res image while high-res one is loading We need to have two elements in the DOM inside the lightbox because otherwise the image flickers when we change the src. I removed the src and srcset attributes from the responsive image when the larger one is loaded to signal that the low-res one is no longer in use. * Add logic to preload image on hover * Update tests * PHP format * Prevent responsive images from being loaded unnecessarily The src attribute on the img elements was causing the srcset attribute to be added as well before the Interactivity API could remove them, causing images to be loaded before they were needed and in the wrong size. This commit removes the src attribute from the img elements before they are output to the DOM, and also updates the tests. * Prevent warning of undefined variable * Prevent warning of undefined variable - refactored * Replace getimagesize() with wp_getimagesize() * Resolve image flash when opening lightbox Rather than allowing the browser to pick the lightbox's responsive image, we now explicitly read the currentSrc attribute from the reference image and also prevent users from opening the lightbox until the reference image is fully loaded. Doing this, we can ensure the zoom animation plays smoothly and there are no oddities in the UX. --------- Co-authored-by: Carlos Bravo --- lib/block-supports/behaviors.php | 61 +++++++++++++-- .../block-library/src/image/interactivity.js | 78 ++++++++++++++----- test/e2e/specs/editor/blocks/image.spec.js | 25 ++++-- 3 files changed, 131 insertions(+), 33 deletions(-) diff --git a/lib/block-supports/behaviors.php b/lib/block-supports/behaviors.php index d577bb6aff7d93..28b097c8f60449 100644 --- a/lib/block-supports/behaviors.php +++ b/lib/block-supports/behaviors.php @@ -88,10 +88,19 @@ function gutenberg_render_behaviors_support_lightbox( $block_content, $block ) { // We want to store the src in the context so we can set it dynamically when the lightbox is opened. $z = new WP_HTML_Tag_Processor( $content ); $z->next_tag( 'img' ); + if ( isset( $block['attrs']['id'] ) ) { - $img_src = wp_get_attachment_url( $block['attrs']['id'] ); + $img_uploaded_src = wp_get_attachment_url( $block['attrs']['id'] ); + $img_metadata = wp_get_attachment_metadata( $block['attrs']['id'] ); + $img_width = $img_metadata['width']; + $img_height = $img_metadata['height']; + $img_uploaded_srcset = wp_get_attachment_image_srcset( $block['attrs']['id'] ); } else { - $img_src = $z->get_attribute( 'src' ); + $img_uploaded_src = $z->get_attribute( 'src' ); + $img_dimensions = wp_getimagesize( $img_uploaded_src ); + $img_width = $img_dimensions[0]; + $img_height = $img_dimensions[1]; + $img_uploaded_srcset = ''; } $w = new WP_HTML_Tag_Processor( $content ); @@ -100,24 +109,59 @@ function gutenberg_render_behaviors_support_lightbox( $block_content, $block ) { $w->set_attribute( 'data-wp-interactive', true ); $w->set_attribute( 'data-wp-context', - sprintf( '{ "core":{ "image": { "initialized": false, "imageSrc": "%s", "lightboxEnabled": false, "lightboxAnimation": "%s", "hideAnimationEnabled": false } } }', $img_src, $lightbox_animation ) + sprintf( + '{ "core": + { "image": + { "imageLoaded": false, + "initialized": false, + "lightboxEnabled": false, + "hideAnimationEnabled": false, + "preloadInitialized": false, + "lightboxAnimation": "%s", + "imageUploadedSrc": "%s", + "imageCurrentSrc": "", + "imageSrcSet": "%s", + "targetWidth": "%s", + "targetHeight": "%s" + } + } + }', + $lightbox_animation, + $img_uploaded_src, + $img_uploaded_srcset, + $img_width, + $img_height + ) ); + $w->next_tag( 'img' ); + $w->set_attribute( 'data-wp-effect', 'effects.core.image.setCurrentSrc' ); $body_content = $w->get_updated_html(); // Wrap the image in the body content with a button. $img = null; - preg_match( '/]+>/', $content, $img ); + preg_match( '/]+>/', $body_content, $img ); $button = '
- ' + ' . $img[0] . '
'; $body_content = preg_replace( '/]+>/', $button, $body_content ); // Add src to the modal image. $m = new WP_HTML_Tag_Processor( $content ); + $m->next_tag( 'figure' ); + $m->add_class( 'responsive-image' ); $m->next_tag( 'img' ); - $m->set_attribute( 'data-wp-bind--src', 'selectors.core.image.imageSrc' ); - $modal_content = $m->get_updated_html(); + $m->set_attribute( 'src', '' ); + $m->set_attribute( 'data-wp-bind--src', 'selectors.core.image.responsiveImgSrc' ); + $initial_image_content = $m->get_updated_html(); + + $q = new WP_HTML_Tag_Processor( $content ); + $q->next_tag( 'figure' ); + $q->add_class( 'enlarged-image' ); + $q->next_tag( 'img' ); + $q->set_attribute( 'src', '' ); + $q->set_attribute( 'data-wp-bind--src', 'selectors.core.image.enlargedImgSrc' ); + $enlarged_image_content = $q->get_updated_html(); $background_color = esc_attr( wp_get_global_styles( array( 'color', 'background' ) ) ); @@ -143,7 +187,8 @@ function gutenberg_render_behaviors_support_lightbox( $block_content, $block ) { - $modal_content + $initial_image_content + $enlarged_image_content
HTML; diff --git a/packages/block-library/src/image/interactivity.js b/packages/block-library/src/image/interactivity.js index 2ef370496a894e..6560cef9e02439 100644 --- a/packages/block-library/src/image/interactivity.js +++ b/packages/block-library/src/image/interactivity.js @@ -22,32 +22,43 @@ store( { core: { image: { showLightbox: ( { context, event } ) => { + // We can't initialize the lightbox until the reference + // image is loaded, otherwise the UX is broken. + if ( ! context.core.image.imageLoaded ) { + return; + } context.core.image.initialized = true; context.core.image.lastFocusedElement = window.document.activeElement; context.core.image.scrollDelta = 0; + context.core.image.lightboxEnabled = true; + if ( context.core.image.lightboxAnimation === 'zoom' ) { + setZoomStyles( + event.target.nextElementSibling, + context, + event + ); + } + // Hide overflow only when the animation is in progress, + // otherwise the removal of the scrollbars will draw attention + // to itself and look like an error + document.documentElement.classList.add( + 'has-lightbox-open' + ); + // Since the img is hidden and its src not loaded until // the lightbox is opened, let's create an img element on the fly // so we can get the dimensions we need to calculate the styles + context.core.image.preloadInitialized = true; const imgDom = document.createElement( 'img' ); - imgDom.onload = function () { - // Enable the lightbox only after the image - // is loaded to prevent flashing of unstyled content - context.core.image.lightboxEnabled = true; - if ( context.core.image.lightboxAnimation === 'zoom' ) { - setZoomStyles( imgDom, context, event ); - } - - // Hide overflow only when the animation is in progress, - // otherwise the removal of the scrollbars will draw attention - // to itself and look like an error - document.documentElement.classList.add( - 'has-lightbox-open' - ); + context.core.image.activateLargeImage = true; }; - imgDom.setAttribute( 'src', context.core.image.imageSrc ); + imgDom.setAttribute( + 'src', + context.core.image.imageUploadedSrc + ); }, hideLightbox: async ( { context, event } ) => { context.core.image.hideAnimationEnabled = true; @@ -131,9 +142,14 @@ store( { roleAttribute: ( { context } ) => { return context.core.image.lightboxEnabled ? 'dialog' : ''; }, - imageSrc: ( { context } ) => { + responsiveImgSrc: ( { context } ) => { + return context.core.image.activateLargeImage + ? '' + : context.core.image.imageCurrentSrc; + }, + enlargedImgSrc: ( { context } ) => { return context.core.image.initialized - ? context.core.image.imageSrc + ? context.core.image.imageUploadedSrc : ''; }, }, @@ -142,6 +158,30 @@ store( { effects: { core: { image: { + setCurrentSrc: ( { context, ref } ) => { + if ( ref.complete ) { + context.core.image.imageLoaded = true; + context.core.image.imageCurrentSrc = ref.currentSrc; + } else { + ref.addEventListener( 'load', function () { + context.core.image.imageLoaded = true; + context.core.image.imageCurrentSrc = + this.currentSrc; + } ); + } + }, + preloadLightboxImage: ( { context, ref } ) => { + ref.addEventListener( 'mouseover', () => { + if ( ! context.core.image.preloadInitialized ) { + context.core.image.preloadInitialized = true; + const imgDom = document.createElement( 'img' ); + imgDom.setAttribute( + 'src', + context.core.image.imageUploadedSrc + ); + } + } ); + }, initLightbox: async ( { context, ref } ) => { context.core.image.figureRef = ref.querySelector( 'figure' ); @@ -163,8 +203,8 @@ store( { } ); function setZoomStyles( imgDom, context, event ) { - let targetWidth = imgDom.naturalWidth; - let targetHeight = imgDom.naturalHeight; + let targetWidth = context.core.image.targetWidth; + let targetHeight = context.core.image.targetHeight; const verticalPadding = 40; diff --git a/test/e2e/specs/editor/blocks/image.spec.js b/test/e2e/specs/editor/blocks/image.spec.js index 685b12b3202e65..242aae941411f9 100644 --- a/test/e2e/specs/editor/blocks/image.spec.js +++ b/test/e2e/specs/editor/blocks/image.spec.js @@ -842,13 +842,19 @@ test.describe( 'Image - interactivity', () => { const lightbox = page.locator( '.wp-lightbox-overlay' ); await expect( lightbox ).toBeHidden(); - const image = lightbox.locator( 'img' ); + const responsiveImage = lightbox.locator( '.responsive-image img' ); + const enlargedImage = lightbox.locator( '.enlarged-image img' ); - await expect( image ).toHaveAttribute( 'src', '' ); + await expect( responsiveImage ).toHaveAttribute( + 'src', + new RegExp( filename ) + ); + await expect( enlargedImage ).toHaveAttribute( 'src', '' ); await page.getByRole( 'button', { name: 'Enlarge image' } ).click(); - await expect( image ).toHaveAttribute( + await expect( responsiveImage ).toHaveAttribute( 'src', '' ); + await expect( enlargedImage ).toHaveAttribute( 'src', new RegExp( filename ) ); @@ -1076,12 +1082,19 @@ test.describe( 'Image - interactivity', () => { await page.goto( `/?p=${ postId }` ); const lightbox = page.locator( '.wp-lightbox-overlay' ); - const imageDom = lightbox.locator( 'img' ); - await expect( imageDom ).toHaveAttribute( 'src', '' ); + const responsiveImage = lightbox.locator( '.responsive-image img' ); + const enlargedImage = lightbox.locator( '.enlarged-image img' ); + + await expect( responsiveImage ).toHaveAttribute( + 'src', + new RegExp( imgUrl ) + ); + await expect( enlargedImage ).toHaveAttribute( 'src', '' ); await page.getByRole( 'button', { name: 'Enlarge image' } ).click(); - await expect( imageDom ).toHaveAttribute( 'src', imgUrl ); + await expect( responsiveImage ).toHaveAttribute( 'src', '' ); + await expect( enlargedImage ).toHaveAttribute( 'src', imgUrl ); } ); } ); From f5b334e4cfa6de29398fc9b101f5a71079bdbc93 Mon Sep 17 00:00:00 2001 From: Marin Atanasov <8436925+tyxla@users.noreply.github.com> Date: Mon, 26 Jun 2023 17:34:26 +0300 Subject: [PATCH 012/266] Lodash: Refactor away from _.kebabCase() in getCleanTemplatePartSlug (#51906) --- packages/edit-site/src/utils/template-part-create.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/edit-site/src/utils/template-part-create.js b/packages/edit-site/src/utils/template-part-create.js index 3c0fa14cbf7ad1..b81a98d15684a7 100644 --- a/packages/edit-site/src/utils/template-part-create.js +++ b/packages/edit-site/src/utils/template-part-create.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { kebabCase } from 'lodash'; +import { paramCase as kebabCase } from 'change-case'; /** * WordPress dependencies From 2b383d2dbffd2ce81d19dda583f842d82385d6ff Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Mon, 26 Jun 2023 16:25:17 +0100 Subject: [PATCH 013/266] Add UI commands to the post editor (#51900) Co-authored-by: ntsekouras --- packages/edit-post/src/editor.js | 2 + .../src/hooks/commands/use-common-commands.js | 103 ++++++++++++++++++ .../hooks/commands/use-edit-mode-commands.js | 2 +- 3 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 packages/edit-post/src/hooks/commands/use-common-commands.js diff --git a/packages/edit-post/src/editor.js b/packages/edit-post/src/editor.js index 42a393c5fe3242..6ec907bfe4b0b0 100644 --- a/packages/edit-post/src/editor.js +++ b/packages/edit-post/src/editor.js @@ -25,6 +25,7 @@ import Layout from './components/layout'; import EditorInitialization from './components/editor-initialization'; import { store as editPostStore } from './store'; import { unlock } from './lock-unlock'; +import useCommonCommands from './hooks/commands/use-common-commands'; const { ExperimentalEditorProvider } = unlock( editorPrivateApis ); const { getLayoutStyles } = unlock( blockEditorPrivateApis ); @@ -32,6 +33,7 @@ const { useCommands } = unlock( coreCommandsPrivateApis ); function Editor( { postId, postType, settings, initialEdits, ...props } ) { useCommands(); + useCommonCommands(); const { hasFixedToolbar, focusMode, diff --git a/packages/edit-post/src/hooks/commands/use-common-commands.js b/packages/edit-post/src/hooks/commands/use-common-commands.js new file mode 100644 index 00000000000000..796e0665fc2fa3 --- /dev/null +++ b/packages/edit-post/src/hooks/commands/use-common-commands.js @@ -0,0 +1,103 @@ +/** + * WordPress dependencies + */ +import { useSelect, useDispatch } from '@wordpress/data'; +import { __, isRTL } from '@wordpress/i18n'; +import { + code, + cog, + drawerLeft, + drawerRight, + blockDefault, +} from '@wordpress/icons'; +import { useCommand } from '@wordpress/commands'; +import { store as preferencesStore } from '@wordpress/preferences'; +import { store as interfaceStore } from '@wordpress/interface'; + +/** + * Internal dependencies + */ +import { store as editPostStore } from '../../store'; + +export default function useCommonCommands() { + const { openGeneralSidebar, closeGeneralSidebar, switchEditorMode } = + useDispatch( editPostStore ); + const { editorMode, activeSidebar } = useSelect( + ( select ) => ( { + activeSidebar: select( interfaceStore ).getActiveComplementaryArea( + editPostStore.name + ), + editorMode: select( editPostStore ).getEditorMode(), + } ), + [] + ); + const { toggle } = useDispatch( preferencesStore ); + + useCommand( { + name: 'core/open-settings-sidebar', + label: __( 'Toggle settings sidebar' ), + icon: isRTL() ? drawerLeft : drawerRight, + callback: ( { close } ) => { + close(); + if ( activeSidebar === 'edit-post/document' ) { + closeGeneralSidebar(); + } else { + openGeneralSidebar( 'edit-post/document' ); + } + }, + } ); + + useCommand( { + name: 'core/open-block-inspector', + label: __( 'Toggle block inspector' ), + icon: blockDefault, + callback: ( { close } ) => { + close(); + if ( activeSidebar === 'edit-post/block' ) { + closeGeneralSidebar(); + } else { + openGeneralSidebar( 'edit-post/block' ); + } + }, + } ); + + useCommand( { + name: 'core/toggle-distraction-free', + label: __( 'Toggle distraction free' ), + icon: cog, + callback: ( { close } ) => { + toggle( 'core/edit-post', 'distractionFree' ); + close(); + }, + } ); + + useCommand( { + name: 'core/toggle-spotlight-mode', + label: __( 'Toggle spotlight mode' ), + icon: cog, + callback: ( { close } ) => { + toggle( 'core/edit-post', 'focusMode' ); + close(); + }, + } ); + + useCommand( { + name: 'core/toggle-top-toolbar', + label: __( 'Toggle top toolbar' ), + icon: cog, + callback: ( { close } ) => { + toggle( 'core/edit-post', 'fixedToolbar' ); + close(); + }, + } ); + + useCommand( { + name: 'core/toggle-code-editor', + label: __( 'Toggle code editor' ), + icon: code, + callback: ( { close } ) => { + switchEditorMode( editorMode === 'visual' ? 'text' : 'visual' ); + close(); + }, + } ); +} diff --git a/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js b/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js index 63e20b9c22c14e..2466bb2a706c4e 100644 --- a/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js +++ b/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js @@ -179,7 +179,7 @@ function useEditUICommands() { } ); commands.push( { - name: 'core/toggle-distraction-free-mode', + name: 'core/toggle-spotlight-mode', label: __( 'Toggle spotlight mode' ), icon: cog, callback: ( { close } ) => { From 3bfaa5f10a86649a7c39f4cde51d33d9077eac97 Mon Sep 17 00:00:00 2001 From: Rich Tabor Date: Mon, 26 Jun 2023 11:36:30 -0400 Subject: [PATCH 014/266] Fix missing MenuGroup in header more menu (#51860) --- .../components/header-edit-mode/more-menu/index.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/edit-site/src/components/header-edit-mode/more-menu/index.js b/packages/edit-site/src/components/header-edit-mode/more-menu/index.js index 802e9f9439811f..7fe63a343960ae 100644 --- a/packages/edit-site/src/components/header-edit-mode/more-menu/index.js +++ b/packages/edit-site/src/components/header-edit-mode/more-menu/index.js @@ -116,14 +116,14 @@ export default function MoreMenu( { showIconLabels } ) { '\\' ) } /> - - + + Date: Mon, 26 Jun 2023 10:54:39 -0500 Subject: [PATCH 015/266] Refactor use-tab-nav shift+tab to use existing utils (#51817) * Check for truthy values before passing to element block checks --- .../components/writing-flow/use-tab-nav.js | 43 +++++-------------- 1 file changed, 10 insertions(+), 33 deletions(-) diff --git a/packages/block-editor/src/components/writing-flow/use-tab-nav.js b/packages/block-editor/src/components/writing-flow/use-tab-nav.js index b22e57a51e32aa..616da1bc758136 100644 --- a/packages/block-editor/src/components/writing-flow/use-tab-nav.js +++ b/packages/block-editor/src/components/writing-flow/use-tab-nav.js @@ -11,6 +11,7 @@ import { useRef } from '@wordpress/element'; * Internal dependencies */ import { store as blockEditorStore } from '../../store'; +import { isInSameBlock, isInsideRootBlock } from '../../utils/dom'; export default function useTabNav() { const container = useRef(); @@ -116,41 +117,20 @@ export default function useTabNav() { return; } + const nextTabbable = focus.tabbable[ direction ]( event.target ); + // We want to constrain the tabbing to the block and its child blocks. // If the preceding form element is within a different block, // such as two sibling image blocks in the placeholder state, // we want shift + tab from the first form element to move to the image // block toolbar and not the previous image block's form element. - // TODO: Should this become a utility function? - /** - * Determine whether an element is part of or is the selected block. - * - * @param {Object} selectedBlockElement - * @param {Object} element - * @return {boolean} Whether the element is part of or is the selected block. - */ - const isElementPartOfSelectedBlock = ( - selectedBlockElement, - element - ) => { - // Check if the element is or is within the selected block by finding the - // closest element with a data-block attribute and seeing if - // it matches our current selected block ID - const elementBlockId = element - .closest( '[data-block]' ) - ?.getAttribute( 'data-block' ); - const isElementSameBlock = - elementBlockId === getSelectedBlockClientId(); - - // Check if the element is a child of the selected block. This could be a - // child block in a group or column block, etc. - const isElementChildOfBlock = - selectedBlockElement.contains( element ); - - return isElementSameBlock || isElementChildOfBlock; - }; + const currentBlock = event.target.closest( '[data-block]' ); + const isElementPartOfSelectedBlock = + currentBlock && + nextTabbable && + ( isInSameBlock( currentBlock, nextTabbable ) || + isInsideRootBlock( currentBlock, nextTabbable ) ); - const nextTabbable = focus.tabbable[ direction ]( event.target ); // Allow tabbing from the block wrapper to a form element, // and between form elements rendered in a block and its child blocks, // such as inside a placeholder. Form elements are generally @@ -159,10 +139,7 @@ export default function useTabNav() { // future they can be rendered in an iframe or shadow DOM. if ( isFormElement( nextTabbable ) && - isElementPartOfSelectedBlock( - event.target.closest( '[data-block]' ), - nextTabbable - ) + isElementPartOfSelectedBlock ) { return; } From be4a649eadcb4189ce1ea36e97a6970af039f847 Mon Sep 17 00:00:00 2001 From: Rich Tabor Date: Mon, 26 Jun 2023 14:04:55 -0400 Subject: [PATCH 016/266] Improve LinkControl Edit UI (#51712) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Move text above link * Change "URL" label to "Link" * Style tweaks * Add chevron based advanced settings button * Adapt logic for rendering actions and settings * Tweaks * Add proper i18n Co-authored-by: Ben Dwyer * Remove commented out style Co-authored-by: Ben Dwyer * Use $button-size-next-default-40px * Add showSettings, combine with new logic * Add additional translation context to advanced * Update toggle drawer name in tests * Standardise query for settings toggle * Update test to check for absence of cancel button during link creation * Fix cancellation tests * Ensure label is always “Link” but remains hidden when it’s the only visible control * Update tests to look for “Link” instead of “URL” name for input * Update empty value UI tests to only run for editing as opposed to creating links * Fix e2e test tabbing order * Use updated terms * Select settings toggle by text not aria label * Fix another tabbing order bug * Fix one more tabbing issue in e2e tests * Fix final tab ordering e2e test * Decouple conditions for showing action buttons from settings Settings may not be provided but action buttons are always needed * Tweak styling to account for action buttons when there are no settings provided * Fix test * Fix e2e test * Update name of the combobox * Fix test expecting Submit button on creation * Fix test by testing under edit rather than creation conditions * Rename URL to Link and avoid triggering command centre * move test earlier --------- Co-authored-by: Ben Dwyer Co-authored-by: Dave Smith --- .../src/components/link-control/index.js | 60 ++--- .../components/link-control/search-input.js | 7 +- .../link-control/settings-drawer.js | 11 +- .../src/components/link-control/style.scss | 115 +++++----- .../src/components/link-control/test/index.js | 216 ++++++++---------- .../media-replace-flow/test/index.js | 2 +- .../src/components/url-input/index.js | 2 + .../specs/editor/various/links.test.js | 29 ++- test/e2e/specs/editor/blocks/buttons.spec.js | 18 +- test/e2e/specs/editor/blocks/links.spec.js | 47 ++-- .../specs/editor/blocks/navigation.spec.js | 12 +- 11 files changed, 270 insertions(+), 249 deletions(-) diff --git a/packages/block-editor/src/components/link-control/index.js b/packages/block-editor/src/components/link-control/index.js index 4803b9a0efebc4..41455c4ecf9704 100644 --- a/packages/block-editor/src/components/link-control/index.js +++ b/packages/block-editor/src/components/link-control/index.js @@ -292,7 +292,8 @@ function LinkControl( { const shownUnlinkControl = onRemove && value && ! isEditingLink && ! isCreatingPage; - const showSettings = !! settings?.length; + const showSettings = !! settings?.length && isEditingLink && hasLinkValue; + const showActions = isEditingLink && hasLinkValue; // Only show text control once a URL value has been committed // and it isn't just empty whitespace. @@ -322,6 +323,18 @@ function LinkControl( { 'has-text-control': showTextControl, } ) } > + { showTextControl && ( + + ) } - { showTextControl && ( - - ) } { errorMessage && ( ) } - { isEditing && ( + { showSettings && (
- { showSettings && ( + { ! currentInputIsEmpty && ( ) } +
+ ) } -
- - -
+ { showActions && ( +
+ +
) } diff --git a/packages/block-editor/src/components/link-control/search-input.js b/packages/block-editor/src/components/link-control/search-input.js index 4e30daaa96524b..d6023b9220d630 100644 --- a/packages/block-editor/src/components/link-control/search-input.js +++ b/packages/block-editor/src/components/link-control/search-input.js @@ -46,7 +46,7 @@ const LinkControlSearchInput = forwardRef( suggestionsQuery = {}, withURLSuggestion = true, createSuggestionButtonText, - useLabel = false, + hideLabelFromVision = false, }, ref ) => { @@ -120,7 +120,7 @@ const LinkControlSearchInput = forwardRef( }; const inputClasses = classnames( className, { - 'has-no-label': ! useLabel, + // 'has-no-label': ! hideLabelFromVision, } ); return ( @@ -128,7 +128,8 @@ const LinkControlSearchInput = forwardRef( setSettingsOpen( ! settingsOpen ) } - icon={ settingsIcon } - label={ __( 'Link Settings' ) } + icon={ isRTL() ? chevronLeftSmall : chevronRightSmall } aria-controls={ settingsDrawerId } - /> + > + { _x( 'Advanced', 'Additional link settings' ) } + { settingsOpen && ( .components-base-control__field { - display: flex; - align-items: center; - } - - .components-base-control__label { - margin-right: $grid-unit-20; - margin-bottom: 0; - min-width: 29px; // align with search results. - } - input[type="text"], // Specificity overide of URLInput defaults. &.block-editor-url-input input[type="text"].block-editor-url-input__input { @include input-control; - width: calc(100% - #{$grid-unit-20 * 2}); display: block; - padding: $grid-unit-10 $grid-unit-20; - margin: 0; - position: relative; border: 1px solid $gray-600; - height: 40px; border-radius: $radius-block-ui; + height: $button-size-next-default-40px; // components do not properly support unstable-large yet. + margin: 0; + padding: $grid-unit-10 $grid-unit-20; + position: relative; + width: 100%; } } @@ -96,9 +84,37 @@ $preview-image-height: 140px; flex-direction: row-reverse; // put "Cancel" on the left but retain DOM order. justify-content: flex-start; gap: $grid-unit-10; + padding: $grid-unit-10; order: 20; } +.block-editor-link-control__search-results-wrapper { + position: relative; + + &::before, + &::after { + content: ""; + position: absolute; + left: -1px; + right: $grid-unit-20; // avoid overlaying scrollbars + display: block; + pointer-events: none; + z-index: 100; + } + + &::before { + height: $grid-unit-20 * 0.5; + top: 0; + bottom: auto; + } + + &::after { + height: $grid-unit-20; + bottom: 0; + top: auto; + } +} + .block-editor-link-control__search-results { margin-top: -$grid-unit-20; padding: $grid-unit-10; @@ -363,36 +379,7 @@ $preview-image-height: 140px; display: flex; // allow for ordering. flex-direction: column; flex-basis: 100%; // occupy full width. - margin-top: $grid-unit-20; - padding-top: $grid-unit-20; position: relative; - - &::after { - content: ""; - display: block; - height: 1px; - background-color: $gray-300; - position: absolute; - left: -$grid-unit-20; - right: -$grid-unit-20; - top: 0; - } -} - -.block-editor-link-control__tools { - display: flex; - flex-wrap: wrap; - align-items: center; - justify-content: space-between; - margin: 0; - padding: $grid-unit-20; - - // To hide the horizontal scrollbar on toggle. - // Margin and padding are needed to prevent cutoff of the toggle button focus outline. - // See: https://github.com/WordPress/gutenberg/pull/47986 - margin-top: calc(var(--wp-admin-border-width-focus) * -1); - padding-top: var(--wp-admin-border-width-focus); - overflow: hidden; } .block-editor-link-control__unlink { @@ -400,17 +387,10 @@ $preview-image-height: 140px; padding-right: $grid-unit-20; } -.block-editor-link-control__settings { - flex: 1; - margin: 0; - - .is-alternate & { - border-top: $border-width solid $gray-900; - } -} - .block-editor-link-control__setting { margin-bottom: $grid-unit-20; + flex: 1; + padding: $grid-unit-10 0 $grid-unit-10 $grid-unit-30; // Cancel left margin inherited from WP Admin Forms CSS. input { @@ -422,6 +402,31 @@ $preview-image-height: 140px; } } +.block-editor-link-control__tools { + padding: $grid-unit-10 $grid-unit-10 0 $grid-unit-10; + margin-top: #{$grid-unit-20 * -1}; + + .components-button.block-editor-link-control__drawer-toggle { + padding-left: 0; + gap: 0; + + // Point downwards when open (same as list view expander) + &[aria-expanded="true"] svg { + visibility: visible; + transition: transform 0.1s ease; + transform: rotate(90deg); + @include reduce-motion("transition"); + } + // Point rightwards when closed (same as list view expander) + &[aria-expanded="false"] svg { + visibility: visible; + transform: rotate(0deg); + transition: transform 0.1s ease; + @include reduce-motion("transition"); + } + } +} + .block-editor-link-control .block-editor-link-control__search-input .components-spinner { display: block; diff --git a/packages/block-editor/src/components/link-control/test/index.js b/packages/block-editor/src/components/link-control/test/index.js index 8e4cd1634b2e73..82bb82a0cfc51d 100644 --- a/packages/block-editor/src/components/link-control/test/index.js +++ b/packages/block-editor/src/components/link-control/test/index.js @@ -138,7 +138,7 @@ describe( 'Basic rendering', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); expect( searchInput ).toBeVisible(); } ); @@ -147,7 +147,7 @@ describe( 'Basic rendering', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); expect( searchInput ).toBeVisible(); // Make sure we use the ARIA 1.0 pattern with aria-owns. @@ -170,7 +170,7 @@ describe( 'Basic rendering', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); // Simulate searching for a term. await user.type( searchInput, 'Hello' ); @@ -283,7 +283,7 @@ describe( 'Basic rendering', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); // Simulate searching for a term. await user.type( searchInput, searchTerm ); @@ -296,7 +296,7 @@ describe( 'Basic rendering', () => { render( ); expect( - screen.queryByRole( 'combobox', { name: 'URL' } ) + screen.queryByRole( 'combobox', { name: 'Link' } ) ).not.toBeInTheDocument(); } ); @@ -309,7 +309,7 @@ describe( 'Basic rendering', () => { ); expect( - screen.getByRole( 'combobox', { name: 'URL' } ) + screen.getByRole( 'combobox', { name: 'Link' } ) ).toBeVisible(); } ); @@ -327,7 +327,7 @@ describe( 'Basic rendering', () => { await user.click( editButton ); expect( - screen.getByRole( 'combobox', { name: 'URL' } ) + screen.getByRole( 'combobox', { name: 'Link' } ) ).toBeVisible(); // If passed `forceIsEditingLink` of `false` while editing, should @@ -340,7 +340,7 @@ describe( 'Basic rendering', () => { ); expect( - screen.queryByRole( 'combobox', { name: 'URL' } ) + screen.queryByRole( 'combobox', { name: 'Link' } ) ).not.toBeInTheDocument(); } ); @@ -424,7 +424,7 @@ describe( 'Searching for a link', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); // Simulate searching for a term. await user.type( searchInput, searchTerm ); @@ -448,7 +448,9 @@ describe( 'Searching for a link', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { + name: 'Link', + } ); // Simulate searching for a term. await user.type( searchInput, searchTerm ); @@ -497,7 +499,9 @@ describe( 'Searching for a link', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { + name: 'Link', + } ); // Simulate searching for a term. await user.type( searchInput, searchTerm ); @@ -528,7 +532,7 @@ describe( 'Searching for a link', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); // Simulate searching for a term. await user.type( searchInput, searchTerm ); @@ -571,7 +575,7 @@ describe( 'Searching for a link', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); // Simulate searching for a term. await user.type( searchInput, 'anything' ); @@ -588,7 +592,7 @@ describe( 'Searching for a link', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); // Simulate searching for a term. await user.type( searchInput, searchTerm ); @@ -618,7 +622,7 @@ describe( 'Searching for a link', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); // Simulate searching for a term. await user.type( searchInput, 'couldbeurlorentitysearchterm' ); @@ -648,7 +652,9 @@ describe( 'Manual link entry', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { + name: 'Link', + } ); // Simulate searching for a term. await user.type( searchInput, searchTerm ); @@ -683,19 +689,9 @@ describe( 'Manual link entry', () => { // Search Input UI. const searchInput = screen.getByRole( 'combobox', { - name: 'URL', + name: 'Link', } ); - let submitButton = screen.getByRole( 'button', { - name: 'Save', - } ); - - expect( submitButton ).toHaveAttribute( - 'aria-disabled', - 'true' - ); - expect( submitButton ).toBeVisible(); - if ( searchString.length ) { // Simulate searching for a term. await user.type( searchInput, searchString ); @@ -707,102 +703,74 @@ describe( 'Manual link entry', () => { // Attempt to submit the empty search value in the input. await user.keyboard( '[Enter]' ); - submitButton = screen.getByRole( 'button', { - name: 'Save', - } ); - - // Verify the UI hasn't allowed submission. + // Verify the UI hasn't allowed submission because + // the search input is still visible. expect( searchInput ).toBeVisible(); - expect( submitButton ).toHaveAttribute( - 'aria-disabled', - 'true' - ); - expect( submitButton ).toBeVisible(); } ); it.each( testTable )( - 'should not allow creation of links %s via the UI "submit" button', + 'should not allow editing of links to a new link %s via the UI "submit" button', async ( _desc, searchString ) => { const user = userEvent.setup(); - render( ); + render( + + ); // Search Input UI. const searchInput = screen.getByRole( 'combobox', { - name: 'URL', + name: 'Link', } ); - let submitButton = screen.queryByRole( 'button', { - name: 'Save', - } ); + // Remove the existing link. + await user.clear( searchInput ); - expect( submitButton ).toHaveAttribute( - 'aria-disabled', - 'true' - ); - expect( submitButton ).toBeVisible(); - - // Simulate searching for a term. if ( searchString.length ) { - // Simulate searching for a term. await user.type( searchInput, searchString ); } else { // Simulate clearing the search term. await user.clear( searchInput ); } - // Attempt to submit the empty search value in the input. - await user.click( submitButton ); - - submitButton = screen.queryByRole( 'button', { + const submitButton = screen.queryByRole( 'button', { name: 'Save', } ); - // Verify the UI hasn't allowed submission. - expect( searchInput ).toBeVisible(); + // debug the UI state + // screen.debug(); + + // Verify the submission UI is disabled. + expect( submitButton ).toBeVisible(); expect( submitButton ).toHaveAttribute( 'aria-disabled', 'true' ); - expect( submitButton ).toBeVisible(); + + // Attempt to submit the empty search value in the input. + await user.click( submitButton ); + + // Verify the UI hasn't allowed submission because + // the search input is still visible. + expect( searchInput ).toBeVisible(); } ); } ); describe( 'Handling cancellation', () => { - it( 'should allow cancellation of the link creation process and reset any entered values', async () => { - const user = userEvent.setup(); + it( 'should not show cancellation button during link creation', async () => { const mockOnRemove = jest.fn(); - const mockOnCancel = jest.fn(); render( ); - // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { - name: 'URL', - } ); - const cancelButton = screen.queryByRole( 'button', { name: 'Cancel', } ); - expect( cancelButton ).toBeEnabled(); - expect( cancelButton ).toBeVisible(); - - // Simulate adding a link for a term. - await user.type( searchInput, 'https://www.wordpress.org' ); - - // Attempt to submit the empty search value in the input. - await user.click( cancelButton ); - - // Verify the consumer can handle the cancellation. - expect( mockOnRemove ).toHaveBeenCalled(); - - // Ensure optional callback is not called. - expect( mockOnCancel ).not.toHaveBeenCalled(); - - expect( searchInput ).toHaveValue( '' ); + expect( cancelButton ).not.toBeInTheDocument(); } ); it( 'should allow cancellation of the link editing process and reset any entered values', async () => { @@ -839,7 +807,7 @@ describe( 'Manual link entry', () => { await toggleSettingsDrawer( user ); let searchInput = screen.getByRole( 'combobox', { - name: 'URL', + name: 'Link', } ); let textInput = screen.getByRole( 'textbox', { @@ -874,7 +842,7 @@ describe( 'Manual link entry', () => { // Re-query the inputs as they have been replaced. searchInput = screen.getByRole( 'combobox', { - name: 'URL', + name: 'Link', } ); textInput = screen.getByRole( 'textbox', { @@ -890,7 +858,13 @@ describe( 'Manual link entry', () => { const user = userEvent.setup(); const mockOnCancel = jest.fn(); - render( ); + render( + + ); const cancelButton = screen.queryByRole( 'button', { name: 'Cancel', @@ -916,7 +890,7 @@ describe( 'Manual link entry', () => { // Search Input UI. const searchInput = screen.getByRole( 'combobox', { - name: 'URL', + name: 'Link', } ); // Simulate searching for a term. @@ -952,7 +926,7 @@ describe( 'Default search suggestions', () => { // Verify input has no value has default suggestions should only show // when this does not have a value. // Search Input UI. - expect( screen.getByRole( 'combobox', { name: 'URL' } ) ).toHaveValue( + expect( screen.getByRole( 'combobox', { name: 'Link' } ) ).toHaveValue( '' ); @@ -981,7 +955,7 @@ describe( 'Default search suggestions', () => { } ); await user.click( currentLinkBtn ); - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); // Search input is set to the URL value. expect( searchInput ).toHaveValue( initialValue.url ); @@ -1003,7 +977,7 @@ describe( 'Default search suggestions', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); // Simulate searching for a term. await user.type( searchInput, searchTerm ); @@ -1041,7 +1015,7 @@ describe( 'Default search suggestions', () => { render( ); - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); const searchResultsField = screen.queryByRole( 'listbox', { name: 'Suggestions', @@ -1099,7 +1073,9 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { + name: 'Link', + } ); // Simulate searching for a term. await user.type( searchInput, entityNameText ); @@ -1166,7 +1142,7 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); // Simulate searching for a term. await user.type( searchInput, 'Some new page to create' ); @@ -1215,7 +1191,7 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); // Simulate searching for a term. await user.type( searchInput, entityNameText ); @@ -1258,7 +1234,7 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); // Simulate searching for a term. await user.type( searchInput, entityNameText ); @@ -1282,7 +1258,7 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => { // Search Input UI. const searchInput = screen.getByRole( 'combobox', { - name: 'URL', + name: 'Link', } ); const searchResultsField = screen.queryByRole( 'listbox' ); @@ -1302,7 +1278,9 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => { ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { + name: 'Link', + } ); const searchResultsField = screen.queryByRole( 'listbox' ); @@ -1325,7 +1303,7 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => { // Search Input UI. const searchInput = screen.getByRole( 'combobox', { - name: 'URL', + name: 'Link', } ); // Simulate searching for a term. @@ -1359,7 +1337,7 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => { render( ); // Search Input UI. - searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); // Simulate searching for a term. await user.type( searchInput, searchText ); @@ -1374,7 +1352,7 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => { await user.click( createButton ); - searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); const errorNotice = screen.getAllByText( 'API response returned invalid entity.' @@ -1447,7 +1425,7 @@ describe( 'Selecting links', () => { // Simulate searching for a term. await user.click( currentLinkBtn ); - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); currentLinkUI = screen.queryByLabelText( 'Currently selected' ); // We should be back to showing the search input. @@ -1488,7 +1466,7 @@ describe( 'Selecting links', () => { // Search Input UI. const searchInput = screen.getByRole( 'combobox', { - name: 'URL', + name: 'Link', } ); // Simulate searching for a term. @@ -1550,7 +1528,7 @@ describe( 'Selecting links', () => { // Search Input UI. const searchInput = screen.getByRole( 'combobox', { - name: 'URL', + name: 'Link', } ); // Simulate searching for a term. @@ -1639,7 +1617,9 @@ describe( 'Selecting links', () => { ).toBeVisible(); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { + name: 'Link', + } ); // Step down into the search results, highlighting the first result item. triggerArrowDown( searchInput ); @@ -1695,7 +1675,9 @@ describe( 'Selecting links', () => { render( ); // focus the search input - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { + name: 'Link', + } ); fireEvent.focus( searchInput ); @@ -1737,10 +1719,7 @@ describe( 'Addition Settings UI', () => { render( ); - const settingsToggle = screen.queryByRole( 'button', { - name: 'Link Settings', - ariaControls: 'link-settings-1', - } ); + const settingsToggle = getSettingsDrawerToggle(); expect( settingsToggle ).not.toBeInTheDocument(); } ); @@ -1757,10 +1736,7 @@ describe( 'Addition Settings UI', () => { const user = userEvent.setup(); - const settingsToggle = screen.queryByRole( 'button', { - name: 'Link Settings', - ariaControls: 'link-settings-1', - } ); + const settingsToggle = getSettingsDrawerToggle(); expect( settingsToggle ).toHaveAttribute( 'aria-expanded', 'false' ); @@ -1921,7 +1897,7 @@ describe( 'Post types', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); // Simulate searching for a term. await user.type( searchInput, searchTerm ); @@ -1948,7 +1924,9 @@ describe( 'Post types', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { + name: 'Link', + } ); // Simulate searching for a term. await user.type( searchInput, searchTerm ); @@ -2406,10 +2384,14 @@ describe( 'Controlling link title text', () => { } ); } ); -async function toggleSettingsDrawer( user ) { - const settingsToggle = screen.queryByRole( 'button', { - name: 'Link Settings', +function getSettingsDrawerToggle() { + return screen.queryByRole( 'button', { + name: 'Advanced', } ); +} + +async function toggleSettingsDrawer( user ) { + const settingsToggle = getSettingsDrawerToggle(); await user.click( settingsToggle ); } diff --git a/packages/block-editor/src/components/media-replace-flow/test/index.js b/packages/block-editor/src/components/media-replace-flow/test/index.js index 2be7e7372260df..cef747ce6be639 100644 --- a/packages/block-editor/src/components/media-replace-flow/test/index.js +++ b/packages/block-editor/src/components/media-replace-flow/test/index.js @@ -128,7 +128,7 @@ describe( 'General media replace flow', () => { ); const mediaURLInput = screen.getByRole( 'combobox', { - name: 'URL', + name: 'Link', expanded: false, } ); diff --git a/packages/block-editor/src/components/url-input/index.js b/packages/block-editor/src/components/url-input/index.js index eb448f79d1b3f5..1451397ce68e5f 100644 --- a/packages/block-editor/src/components/url-input/index.js +++ b/packages/block-editor/src/components/url-input/index.js @@ -434,6 +434,7 @@ class URLInput extends Component { placeholder = __( 'Paste URL or type to search' ), __experimentalRenderControl: renderControl, value = '', + hideLabelFromVision = false, } = this.props; const { @@ -452,6 +453,7 @@ class URLInput extends Component { className: classnames( 'block-editor-url-input', className, { 'is-full-width': isFullWidth, } ), + hideLabelFromVision, }; const inputProps = { diff --git a/packages/e2e-tests/specs/editor/various/links.test.js b/packages/e2e-tests/specs/editor/various/links.test.js index 3be80f786fbc28..719d00afe076bb 100644 --- a/packages/e2e-tests/specs/editor/various/links.test.js +++ b/packages/e2e-tests/specs/editor/various/links.test.js @@ -105,7 +105,8 @@ describe( 'Links', () => { await waitForURLFieldAutoFocus(); const urlInputValue = await page.evaluate( - () => document.querySelector( '[aria-label="URL"]' ).value + () => + document.querySelector( '.block-editor-url-input__input' ).value ); expect( urlInputValue ).toBe( '' ); @@ -496,7 +497,7 @@ describe( 'Links', () => { await pressKeyWithModifier( 'primary', 'K' ); const [ settingsToggle ] = await page.$x( - '//button[contains(@aria-label, "Link Settings")]' + '//button[contains(text(), "Advanced")]' ); await settingsToggle.click(); @@ -528,7 +529,7 @@ describe( 'Links', () => { await waitForURLFieldAutoFocus(); - await page.keyboard.press( 'Tab' ); + await pressKeyWithModifier( 'shift', 'Tab' ); // Tabbing should land us in the text input. const { isTextInput, textValue } = await page.evaluate( () => { @@ -585,8 +586,10 @@ describe( 'Links', () => { await editButton.click(); - // Tabbing forward should land us in the "Text" input. - await page.keyboard.press( 'Tab' ); + await waitForURLFieldAutoFocus(); + + // Tabbing backward should land us in the "Text" input. + await pressKeyWithModifier( 'shift', 'Tab' ); const textInputValue = await page.evaluate( () => document.activeElement.value @@ -614,8 +617,9 @@ describe( 'Links', () => { ); await editButton.click(); - // tab forward to the text input. - await page.keyboard.press( 'Tab' ); + await waitForURLFieldAutoFocus(); + + await pressKeyWithModifier( 'shift', 'Tab' ); const textInputValue = await page.evaluate( () => document.activeElement.value @@ -661,7 +665,7 @@ describe( 'Links', () => { await waitForURLFieldAutoFocus(); const [ settingsToggle ] = await page.$x( - '//button[contains(@aria-label, "Link Settings")]' + '//button[contains(text(), "Advanced")]' ); await settingsToggle.click(); @@ -679,7 +683,7 @@ describe( 'Links', () => { await pressKeyWithModifier( 'shift', 'ArrowRight' ); // Move back to the text input. - await pressKeyTimes( 'Tab', 2 ); + await pressKeyTimes( 'Tab', 1 ); // Tabbing back should land us in the text input. const textInputValue = await page.evaluate( @@ -879,8 +883,11 @@ describe( 'Links', () => { await waitForURLFieldAutoFocus(); - // Move to Link Text field. - await page.keyboard.press( 'Tab' ); + // Move to "Text" field. + await pressKeyWithModifier( 'shift', 'Tab' ); + + // Delete existing value from "Text" field + await page.keyboard.press( 'Delete' ); // Change text to "z" await page.keyboard.type( 'z' ); diff --git a/test/e2e/specs/editor/blocks/buttons.spec.js b/test/e2e/specs/editor/blocks/buttons.spec.js index 8eacb7e2bed2e9..d6bdcb167f9ac9 100644 --- a/test/e2e/specs/editor/blocks/buttons.spec.js +++ b/test/e2e/specs/editor/blocks/buttons.spec.js @@ -10,6 +10,9 @@ test.describe( 'Buttons', () => { test( 'has focus on button content', async ( { editor, page } ) => { await editor.insertBlock( { name: 'core/buttons' } ); + await expect( + editor.canvas.locator( 'role=textbox[name="Button text"i]' ) + ).toBeFocused(); await page.keyboard.type( 'Content' ); // Check the content. @@ -50,9 +53,12 @@ test.describe( 'Buttons', () => { } ) => { // Regression: https://github.com/WordPress/gutenberg/pull/19885 await editor.insertBlock( { name: 'core/buttons' } ); + await expect( + editor.canvas.locator( 'role=textbox[name="Button text"i]' ) + ).toBeFocused(); await pageUtils.pressKeys( 'primary+k' ); await expect( - page.locator( 'role=combobox[name="URL"i]' ) + page.locator( 'role=combobox[name="Link"i]' ) ).toBeFocused(); await page.keyboard.press( 'Escape' ); await expect( @@ -78,9 +84,12 @@ test.describe( 'Buttons', () => { } ) => { // Regression: https://github.com/WordPress/gutenberg/issues/34307 await editor.insertBlock( { name: 'core/buttons' } ); + await expect( + editor.canvas.locator( 'role=textbox[name="Button text"i]' ) + ).toBeFocused(); await pageUtils.pressKeys( 'primary+k' ); await expect( - page.locator( 'role=combobox[name="URL"i]' ) + page.locator( 'role=combobox[name="Link"i]' ) ).toBeFocused(); await page.keyboard.type( 'https://example.com' ); await page.keyboard.press( 'Enter' ); @@ -107,9 +116,12 @@ test.describe( 'Buttons', () => { } ) => { // Regression: https://github.com/WordPress/gutenberg/issues/34307 await editor.insertBlock( { name: 'core/buttons' } ); + await expect( + editor.canvas.locator( 'role=textbox[name="Button text"i]' ) + ).toBeFocused(); await pageUtils.pressKeys( 'primary+k' ); - const urlInput = page.locator( 'role=combobox[name="URL"i]' ); + const urlInput = page.locator( 'role=combobox[name="Link"i]' ); await expect( urlInput ).toBeFocused(); await page.keyboard.type( 'example.com' ); diff --git a/test/e2e/specs/editor/blocks/links.spec.js b/test/e2e/specs/editor/blocks/links.spec.js index 6493b9effe8aab..8443a04d0417e6 100644 --- a/test/e2e/specs/editor/blocks/links.spec.js +++ b/test/e2e/specs/editor/blocks/links.spec.js @@ -28,8 +28,32 @@ test.describe( 'Links', () => { // Type a URL. await page.keyboard.type( 'https://wordpress.org/gutenberg' ); + // Ensure that the contents of the post have not been changed, since at + // this point the link is still not inserted. + await expect.poll( editor.getBlocks ).toMatchObject( [ + { + name: 'core/paragraph', + attributes: { content: 'This is Gutenberg' }, + }, + ] ); + + await page.keyboard.press( 'Enter' ); + + await page.keyboard.press( 'ArrowLeft' ); + await page.keyboard.press( 'ArrowLeft' ); + + // Edit link. + await page.getByRole( 'button', { name: 'Edit' } ).click(); + // Open settings. - await page.getByRole( 'button', { name: 'Link Settings' } ).click(); + await page + .getByRole( 'region', { + name: 'Editor content', + } ) + .getByRole( 'button', { + name: 'Advanced', + } ) + .click(); // Navigate to and toggle the "Open in new tab" checkbox. const checkbox = page.getByLabel( 'Open in new tab' ); @@ -39,15 +63,6 @@ test.describe( 'Links', () => { await expect( checkbox ).toBeChecked(); await expect( checkbox ).toBeFocused(); - // Ensure that the contents of the post have not been changed, since at - // this point the link is still not inserted. - await expect.poll( editor.getBlocks ).toMatchObject( [ - { - name: 'core/paragraph', - attributes: { content: 'This is Gutenberg' }, - }, - ] ); - // Tab back to the Submit and apply the link. await page //TODO: change to a better selector when https://github.com/WordPress/gutenberg/issues/51060 is resolved. @@ -82,11 +97,7 @@ test.describe( 'Links', () => { await pageUtils.pressKeys( 'primary+k' ); await page.keyboard.type( 'w.org' ); - await page - //TODO: change to a better selector when https://github.com/WordPress/gutenberg/issues/51060 is resolved. - .locator( '.block-editor-link-control' ) - .getByRole( 'button', { name: 'Save' } ) - .click(); + await page.keyboard.press( 'Enter' ); await expect.poll( editor.getBlocks ).toMatchObject( [ { @@ -107,7 +118,11 @@ test.describe( 'Links', () => { await page.keyboard.type( 'wordpress.org' ); // Update the link. - await page.keyboard.press( 'Enter' ); + await page + //TODO: change to a better selector when https://github.com/WordPress/gutenberg/issues/51060 is resolved. + .locator( '.block-editor-link-control' ) + .getByRole( 'button', { name: 'Save' } ) + .click(); // Navigate back to the popover. await page.keyboard.press( 'ArrowLeft' ); diff --git a/test/e2e/specs/editor/blocks/navigation.spec.js b/test/e2e/specs/editor/blocks/navigation.spec.js index 13ea27b60e77e8..b5883694e192e5 100644 --- a/test/e2e/specs/editor/blocks/navigation.spec.js +++ b/test/e2e/specs/editor/blocks/navigation.spec.js @@ -902,7 +902,7 @@ test.describe( 'Navigation block', () => { // Immediately dismiss the Link UI thereby not populating the `url` attribute // of the block. - await linkControl.pressCancel(); + await page.keyboard.press( 'Escape' ); // Get the Inspector Tabs. const blockSettings = page.getByRole( 'region', { @@ -1259,18 +1259,10 @@ class LinkControl { getSearchInput() { return this.page.getByRole( 'combobox', { - name: 'URL', + name: 'Link', } ); } - async pressCancel() { - const cancelButton = this.page.getByRole( 'button', { - name: 'Cancel', - } ); - - return cancelButton.click(); - } - async getSearchResults() { const searchInput = this.getSearchInput(); From b10b90ebfbf4330eea41dd035d5b35af9e158cd4 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Mon, 26 Jun 2023 20:47:34 +0200 Subject: [PATCH 017/266] BlockLockModal: restore focus on fallback toolbar button when original button is not rendered (#51666) * useFocusReturn: pass focus restoration default target to the onFocusReturn callback * Modal: pass onFocusReturn callback * BlockLockModal: restore focus to first focusable item when unlocking block from toolbar button * Add comments * Revert changes to `useFocusReturn` and `Modal` component, just add logic to the BlockLockToolbar instead * Comment --- .../src/components/block-lock/toolbar.js | 40 ++++++++++++++++--- .../src/components/block-toolbar/index.js | 5 ++- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/packages/block-editor/src/components/block-lock/toolbar.js b/packages/block-editor/src/components/block-lock/toolbar.js index d0a9d3d8bf3d3d..0abd693b5a7c99 100644 --- a/packages/block-editor/src/components/block-lock/toolbar.js +++ b/packages/block-editor/src/components/block-lock/toolbar.js @@ -3,7 +3,8 @@ */ import { __ } from '@wordpress/i18n'; import { ToolbarButton, ToolbarGroup } from '@wordpress/components'; -import { useReducer } from '@wordpress/element'; +import { focus } from '@wordpress/dom'; +import { useReducer, useRef, useEffect } from '@wordpress/element'; import { lock } from '@wordpress/icons'; /** @@ -12,7 +13,7 @@ import { lock } from '@wordpress/icons'; import BlockLockModal from './modal'; import useBlockLock from './use-block-lock'; -export default function BlockLockToolbar( { clientId } ) { +export default function BlockLockToolbar( { clientId, wrapperRef } ) { const { canEdit, canMove, canRemove, canLock } = useBlockLock( clientId ); const [ isModalOpen, toggleModal ] = useReducer( @@ -20,11 +21,37 @@ export default function BlockLockToolbar( { clientId } ) { false ); - if ( ! canLock ) { - return null; - } + const lockButtonRef = useRef( null ); + const isFirstRender = useRef( true ); + + const shouldHideBlockLockUI = + ! canLock || ( canEdit && canMove && canRemove ); + + // Restore focus manually on the first focusable element in the toolbar + // when the block lock modal is closed and the block is not locked anymore. + // See https://github.com/WordPress/gutenberg/issues/51447 + useEffect( () => { + if ( isFirstRender.current ) { + isFirstRender.current = false; + return; + } + + if ( ! isModalOpen && shouldHideBlockLockUI ) { + focus.focusable + .find( wrapperRef.current, { + sequential: false, + } ) + .find( + ( element ) => + element.tagName === 'BUTTON' && + element !== lockButtonRef.current + ) + ?.focus(); + } + // wrapperRef is a reference object and should be stable + }, [ isModalOpen, shouldHideBlockLockUI, wrapperRef ] ); - if ( canEdit && canMove && canRemove ) { + if ( shouldHideBlockLockUI ) { return null; } @@ -35,6 +62,7 @@ export default function BlockLockToolbar( { clientId } ) { icon={ lock } label={ __( 'Unlock' ) } onClick={ toggleModal } + ref={ lockButtonRef } /> { isModalOpen && ( diff --git a/packages/block-editor/src/components/block-toolbar/index.js b/packages/block-editor/src/components/block-toolbar/index.js index 29726ea6e3d3d5..0f968b8a209a73 100644 --- a/packages/block-editor/src/components/block-toolbar/index.js +++ b/packages/block-editor/src/components/block-toolbar/index.js @@ -78,6 +78,8 @@ const BlockToolbar = ( { hideDragHandle } ) => { }; }, [] ); + const toolbarWrapperRef = useRef( null ); + // Handles highlighting the current block outline on hover or focus of the // block type toolbar area. const { toggleBlockHighlight } = useDispatch( blockEditorStore ); @@ -123,7 +125,7 @@ const BlockToolbar = ( { hideDragHandle } ) => { } ); return ( -
+
{ ! isMultiToolbar && isLargeViewport && blockEditingMode === 'default' && } @@ -135,6 +137,7 @@ const BlockToolbar = ( { hideDragHandle } ) => { { ! isMultiToolbar && ( ) } Date: Tue, 27 Jun 2023 10:15:13 +1000 Subject: [PATCH 018/266] Don't show 'Back to page' notification when navigating away from page (#51880) --- .../back-to-page-notification.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/edit-site/src/components/page-content-focus-manager/back-to-page-notification.js b/packages/edit-site/src/components/page-content-focus-manager/back-to-page-notification.js index 171d4b6a5f7579..a2990c56a673cf 100644 --- a/packages/edit-site/src/components/page-content-focus-manager/back-to-page-notification.js +++ b/packages/edit-site/src/components/page-content-focus-manager/back-to-page-notification.js @@ -25,8 +25,11 @@ export default function BackToPageNotification() { * switches from focusing on editing page content to editing a template. */ export function useBackToPageNotification() { - const hasPageContentFocus = useSelect( - ( select ) => select( editSiteStore ).hasPageContentFocus(), + const { isPage, hasPageContentFocus } = useSelect( + ( select ) => ( { + isPage: select( editSiteStore ).isPage(), + hasPageContentFocus: select( editSiteStore ).hasPageContentFocus(), + } ), [] ); @@ -39,6 +42,7 @@ export function useBackToPageNotification() { useEffect( () => { if ( ! alreadySeen.current && + isPage && prevHasPageContentFocus.current && ! hasPageContentFocus ) { @@ -57,6 +61,7 @@ export function useBackToPageNotification() { prevHasPageContentFocus.current = hasPageContentFocus; }, [ alreadySeen, + isPage, prevHasPageContentFocus, hasPageContentFocus, createInfoNotice, From 4ba84e384dedaebd8ccc08eea08fd9f8b31687b4 Mon Sep 17 00:00:00 2001 From: Robert Anderson Date: Tue, 27 Jun 2023 10:18:32 +1000 Subject: [PATCH 019/266] useBlockSync(): Reset inner blocks when component unmounts (#51783) --- .../provider/test/use-block-sync.js | 27 ++++++++++++++----- .../src/components/provider/use-block-sync.js | 19 +++++++++++++ 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/packages/block-editor/src/components/provider/test/use-block-sync.js b/packages/block-editor/src/components/provider/test/use-block-sync.js index 7901c3d98f3a92..b1e6a97d137c91 100644 --- a/packages/block-editor/src/components/provider/test/use-block-sync.js +++ b/packages/block-editor/src/components/provider/test/use-block-sync.js @@ -48,7 +48,7 @@ describe( 'useBlockSync hook', () => { jest.clearAllMocks(); } ); - it( 'resets the block-editor blocks when the controll value changes', async () => { + it( 'resets the block-editor blocks when the controlled value changes', async () => { const fakeBlocks = []; const resetBlocks = jest.spyOn( blockEditorActions, 'resetBlocks' ); const replaceInnerBlocks = jest.spyOn( @@ -58,7 +58,7 @@ describe( 'useBlockSync hook', () => { const onChange = jest.fn(); const onInput = jest.fn(); - const { rerender } = render( + const { rerender, unmount } = render( { expect( onInput ).not.toHaveBeenCalled(); expect( replaceInnerBlocks ).not.toHaveBeenCalled(); expect( resetBlocks ).toHaveBeenCalledWith( testBlocks ); + + unmount(); + + expect( onChange ).not.toHaveBeenCalled(); + expect( onInput ).not.toHaveBeenCalled(); + expect( replaceInnerBlocks ).not.toHaveBeenCalled(); + expect( resetBlocks ).toHaveBeenCalledWith( [] ); } ); - it( 'replaces the inner blocks of a block when the control value changes if a clientId is passed', async () => { + it( 'replaces the inner blocks of a block when the controlled value changes if a clientId is passed', async () => { const fakeBlocks = []; const replaceInnerBlocks = jest.spyOn( blockEditorActions, @@ -100,7 +107,7 @@ describe( 'useBlockSync hook', () => { const onChange = jest.fn(); const onInput = jest.fn(); - const { rerender } = render( + const { rerender, unmount } = render( { expect( onChange ).not.toHaveBeenCalled(); expect( onInput ).not.toHaveBeenCalled(); expect( resetBlocks ).not.toHaveBeenCalled(); - // We can't check the args because the blocks are cloned. - expect( replaceInnerBlocks ).toHaveBeenCalled(); + expect( replaceInnerBlocks ).toHaveBeenCalledWith( 'test', [ + expect.objectContaining( { name: 'test/test-block' } ), + ] ); + + unmount(); + + expect( onChange ).not.toHaveBeenCalled(); + expect( onInput ).not.toHaveBeenCalled(); + expect( resetBlocks ).not.toHaveBeenCalled(); + expect( replaceInnerBlocks ).toHaveBeenCalledWith( 'test', [] ); } ); it( 'does not add the controlled blocks to the block-editor store if the store already contains them', async () => { diff --git a/packages/block-editor/src/components/provider/use-block-sync.js b/packages/block-editor/src/components/provider/use-block-sync.js index f7392f99035a95..d788c7b4442304 100644 --- a/packages/block-editor/src/components/provider/use-block-sync.js +++ b/packages/block-editor/src/components/provider/use-block-sync.js @@ -134,6 +134,19 @@ export default function useBlockSync( { } }; + // Clean up the changes made by setControlledBlocks() when the component + // containing useBlockSync() unmounts. + const unsetControlledBlocks = () => { + __unstableMarkNextChangeAsNotPersistent(); + if ( clientId ) { + setHasControlledInnerBlocks( clientId, false ); + __unstableMarkNextChangeAsNotPersistent(); + replaceInnerBlocks( clientId, [] ); + } else { + resetBlocks( [] ); + } + }; + // Add a subscription to the block-editor registry to detect when changes // have been made. This lets us inform the data source of changes. This // is an effect so that the subscriber can run synchronously without @@ -287,4 +300,10 @@ export default function useBlockSync( { unsubscribe(); }; }, [ registry, clientId ] ); + + useEffect( () => { + return () => { + unsetControlledBlocks(); + }; + }, [] ); } From e21370b762054bd9bc70f44b36967c155d66c462 Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Tue, 27 Jun 2023 10:54:54 +1000 Subject: [PATCH 020/266] Keep framer-motion from updating minor version (#51894) * Keep framer-motion from updating minor version * Revert unnecessary package-lock changes --- package-lock.json | 716 +++++++++++++++---------------- packages/components/package.json | 2 +- 2 files changed, 359 insertions(+), 359 deletions(-) diff --git a/package-lock.json b/package-lock.json index 79effbe979b53e..946b738688b0b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10455,7 +10455,7 @@ "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", "dev": true, "requires": { "ansi-regex": "^2.0.0" @@ -13585,7 +13585,7 @@ "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", "dev": true, "requires": { "ansi-regex": "^2.0.0" @@ -16068,7 +16068,7 @@ "@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, "@types/keyv": { @@ -17451,7 +17451,7 @@ "dom-scroll-into-view": "^1.2.1", "downshift": "^6.0.15", "fast-deep-equal": "^3.1.3", - "framer-motion": "^10.11.6", + "framer-motion": "~10.11.6", "gradient-parser": "^0.1.5", "highlight-words-core": "^1.2.2", "is-plain-object": "^5.0.0", @@ -18807,7 +18807,7 @@ "absolute-path": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/absolute-path/-/absolute-path-0.0.0.tgz", - "integrity": "sha1-p4di+9rftSl76ZsV01p4Wy8JW/c=" + "integrity": "sha512-HQiug4c+/s3WOvEnDRxXVmNtSG5s2gJM9r19BTcqjp7BWcE48PB+Y2G6jE65kqI0LpsQeMZygt/b60Gi4KxGyA==" }, "accepts": { "version": "1.3.7", @@ -25849,7 +25849,7 @@ "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" } } }, @@ -25874,7 +25874,7 @@ "arr-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==" }, "arr-flatten": { "version": "1.1.0", @@ -25884,7 +25884,7 @@ "arr-union": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==" }, "array-differ": { "version": "3.0.0", @@ -25895,20 +25895,20 @@ "array-find-index": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", "dev": true, "optional": true }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "dev": true }, "array-ify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", - "integrity": "sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=", + "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", "dev": true }, "array-includes": { @@ -26159,7 +26159,7 @@ "array-union": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", "dev": true, "requires": { "array-uniq": "^1.0.1" @@ -26168,13 +26168,13 @@ "array-uniq": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", "dev": true }, "array-unique": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" + "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==" }, "array.prototype.flat": { "version": "1.2.1", @@ -26918,7 +26918,7 @@ "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" }, "asn1": { "version": "0.2.3", @@ -26959,13 +26959,13 @@ "inherits": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "integrity": "sha512-8nWq2nLTAwd02jTqJExUYFSD/fKq6VH9Y/oG2accc/kdI0V98Bag8d5a4gi3XHz73rDWa2PvTtvcWYquKqSENA==", "dev": true }, "util": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "integrity": "sha512-5KiHfsmkqacuKjkRkdV7SsfDJ2EGiPsK92s2MhNSY0craxjTdKTtqKsJaCWp4LW33ZZ0OPUv1WO/TFvNQRiQxQ==", "dev": true, "requires": { "inherits": "2.0.1" @@ -26976,13 +26976,13 @@ "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", "dev": true }, "assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" + "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==" }, "ast-types": { "version": "0.14.2", @@ -26995,7 +26995,7 @@ "ast-types-flow": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", - "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", + "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==", "dev": true }, "astral-regex": { @@ -27023,7 +27023,7 @@ "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true }, "at-least-node": { @@ -27040,7 +27040,7 @@ "atob-lite": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/atob-lite/-/atob-lite-2.0.0.tgz", - "integrity": "sha1-D+9a1G8b16hQLGVyfwNn1e5D1pY=", + "integrity": "sha512-LEeSAWeh2Gfa2FtlQE1shxQ8zi5F9GHarrGKz08TMdODD5T4eH6BMsvtnhbWZ+XQn+Gb6om/917ucvRu7l7ukw==", "dev": true }, "autoprefixer": { @@ -27110,7 +27110,7 @@ "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", "dev": true }, "aws4": { @@ -27543,7 +27543,7 @@ "babel-plugin-transform-remove-console": { "version": "6.9.4", "resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-console/-/babel-plugin-transform-remove-console-6.9.4.tgz", - "integrity": "sha1-uYA2DAZzhOJLNXpYjYB9PINSd4A=", + "integrity": "sha512-88blrUrMX3SPiGkT1GnvVY8E/7A+k6oj3MNvUtTIxJflFzXTw1bHkuJ/y039ouhFMp2prRn5cQGzokViYi1dsg==", "dev": true }, "babel-preset-current-node-syntax": { @@ -27638,7 +27638,7 @@ "define-property": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", "requires": { "is-descriptor": "^1.0.0" } @@ -27679,7 +27679,7 @@ "batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", "dev": true }, "batch-processor": { @@ -27691,7 +27691,7 @@ "bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", "dev": true, "optional": true, "requires": { @@ -27707,7 +27707,7 @@ "benchmark": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz", - "integrity": "sha1-CfPeMckWQl1JjMLuVloOvzwqVik=", + "integrity": "sha512-l9MlfN4M1K/H2fbhfMy3B7vJd6AGKJVQn2h6Sg/Yx+KckoUA7ewS5Vv6TjSq18ooE1kS9hhAlQRH3AkXIh/aOQ==", "dev": true, "requires": { "lodash": "^4.17.4", @@ -27893,7 +27893,7 @@ "boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" }, "boxen": { "version": "5.1.2", @@ -28019,7 +28019,7 @@ "brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", "dev": true }, "browser-assert": { @@ -28161,7 +28161,7 @@ "btoa-lite": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz", - "integrity": "sha1-M3dm2hWAEhD92VbCLpxokaudAzc=", + "integrity": "sha512-gvW7InbIyF8AicrqWoptdW08pUxuhq8BEgowNajy9RhiE86fmGAGl+bLKo6oB8QP0CkqHLowfN0oJdKC/J6LbA==", "dev": true }, "buffer": { @@ -28178,7 +28178,7 @@ "buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "dev": true }, "buffer-from": { @@ -28195,7 +28195,7 @@ "buffer-xor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", "dev": true }, "builtin-modules": { @@ -28207,13 +28207,13 @@ "builtin-status-codes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==", "dev": true }, "builtins": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", - "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=", + "integrity": "sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==", "dev": true }, "byte-size": { @@ -28460,7 +28460,7 @@ "caller-callsite": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", - "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", + "integrity": "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==", "requires": { "callsites": "^2.0.0" }, @@ -28468,14 +28468,14 @@ "callsites": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", - "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=" + "integrity": "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==" } } }, "caller-path": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", - "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "integrity": "sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==", "requires": { "caller-callsite": "^2.0.0" } @@ -28483,7 +28483,7 @@ "callsite": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", - "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", + "integrity": "sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ==", "dev": true }, "callsites": { @@ -28514,7 +28514,7 @@ "camelcase-keys": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", - "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "integrity": "sha512-bA/Z/DERHKqoEOrp+qeGKw1QlvEQkGZSc0XaY6VnTxZr+Kv1G5zFwttpjv8qxZ/sBPT4nthwZaAcsAZTJlSKXQ==", "dev": true, "optional": true, "requires": { @@ -28525,7 +28525,7 @@ "camelcase": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "integrity": "sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw==", "dev": true, "optional": true } @@ -28581,7 +28581,7 @@ "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", "dev": true }, "ccount": { @@ -28900,7 +28900,7 @@ "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", "requires": { "is-descriptor": "^0.1.0" } @@ -29094,7 +29094,7 @@ "cli-truncate": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz", - "integrity": "sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ=", + "integrity": "sha512-f4r4yJnbT++qUPI9NR4XLDLq41gQ+uqnPItWG0F5ZkehuNiTTa3EY0S4AqTSUOeJ7/zU41oWPQSNkW5BqPL9bg==", "dev": true, "requires": { "slice-ansi": "0.0.4", @@ -29104,13 +29104,13 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", "dev": true }, "is-fullwidth-code-point": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", "dev": true, "requires": { "number-is-nan": "^1.0.0" @@ -29119,7 +29119,7 @@ "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", "dev": true, "requires": { "code-point-at": "^1.0.0", @@ -29130,7 +29130,7 @@ "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", "dev": true, "requires": { "ansi-regex": "^2.0.0" @@ -29202,7 +29202,7 @@ "clone": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=" + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==" }, "clone-deep": { "version": "4.0.1", @@ -29277,7 +29277,7 @@ "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", "dev": true }, "collapse-white-space": { @@ -29295,7 +29295,7 @@ "collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==", "requires": { "map-visit": "^1.0.0", "object-visit": "^1.0.0" @@ -29442,7 +29442,7 @@ "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" }, "compare-func": { "version": "2.0.0", @@ -29554,12 +29554,12 @@ "computed-style": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/computed-style/-/computed-style-0.1.4.tgz", - "integrity": "sha1-fzRP2FhLLkJb7cpKGvwOMAuwXXQ=" + "integrity": "sha512-WpAmaKbMNmS3OProfHIdJiNleNJdgUrJfbKArXua28QF7+0CoZjlLn0lp6vlc+dl5r2/X9GQiQRQQU4BzSa69w==" }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "concat-stream": { "version": "1.6.2", @@ -29576,7 +29576,7 @@ "concurrently": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-3.5.0.tgz", - "integrity": "sha1-jPG3cHppFqeKT/W3e7BN7FSzebI=", + "integrity": "sha512-Z2iVM5+c0VxKmENTXrG/kp+MUhWEEH+wI5wV/L8CTFJDb/uae1zSVIkNM7o3W4Tdt42pv7RGsOICaskWy9bqSA==", "dev": true, "requires": { "chalk": "0.5.1", @@ -29592,19 +29592,19 @@ "ansi-regex": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", - "integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=", + "integrity": "sha512-sGwIGMjhYdW26/IhwK2gkWWI8DRCVO6uj3hYgHT+zD+QL1pa37tM3ujhyfcJIYSbsxp7Gxhy7zrRW/1AHm4BmA==", "dev": true }, "ansi-styles": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz", - "integrity": "sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94=", + "integrity": "sha512-f2PKUkN5QngiSemowa6Mrk9MPCdtFiOSmibjZ+j1qhLGHHYsqZwmBMRF3IRMVXo8sybDqx2fJl2d/8OphBoWkA==", "dev": true }, "chalk": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", - "integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=", + "integrity": "sha512-bIKA54hP8iZhyDT81TOsJiQvR1gW+ZYSXFaZUAvoD4wCHdbHY2actmpTE4x344ZlFqHbvoxKOaESULTZN2gstg==", "dev": true, "requires": { "ansi-styles": "^1.1.0", @@ -29617,7 +29617,7 @@ "supports-color": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz", - "integrity": "sha1-2S3iaU6z9nMjlz1649i1W0wiGQo=", + "integrity": "sha512-tdCZ28MnM7k7cJDJc7Eq80A9CsRFAAOZUy41npOZCs++qSjfIy7o5Rh46CBk+Dk5FbKJ33X3Tqg4YrV07N5RaA==", "dev": true } } @@ -29625,7 +29625,7 @@ "commander": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.6.0.tgz", - "integrity": "sha1-nfflL7Kgyw+4kFjugMMQQiXzfh0=", + "integrity": "sha512-PhbTMT+ilDXZKqH8xbvuUY2ZEQNef0Q7DKxgoEKb4ccytsdvVVJmYqR0sGbi96nxU6oGrwEIQnclpK2NBZuQlg==", "dev": true }, "date-fns": { @@ -29646,13 +29646,13 @@ "has-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "integrity": "sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==", "dev": true }, "strip-ansi": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", - "integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=", + "integrity": "sha512-DerhZL7j6i6/nEnVG0qViKXI0OKouvvpsAiaj7c+LfqZZZxdwZtv8+UiA/w4VUJpT8UzX0pR1dcHOii1GbmruQ==", "dev": true, "requires": { "ansi-regex": "^0.2.1" @@ -29661,7 +29661,7 @@ "supports-color": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "integrity": "sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==", "dev": true, "requires": { "has-flag": "^1.0.0" @@ -29715,7 +29715,7 @@ "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", "dev": true }, "constant-case": { @@ -29731,7 +29731,7 @@ "constants-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==", "dev": true }, "content-type": { @@ -30066,7 +30066,7 @@ "cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", "dev": true }, "copy-concurrently": { @@ -30111,7 +30111,7 @@ "copy-descriptor": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" + "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==" }, "copy-dir": { "version": "1.3.0", @@ -30687,7 +30687,7 @@ "cross-env": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-3.2.4.tgz", - "integrity": "sha1-ngWF8neGTtQhznVvgamA/w1piro=", + "integrity": "sha512-T8AFEAiuJ0w53ou6rnu3Fipaiu1W6ZO9GYfd33uxe1kAIiXM0fD8QnIm7orcJBOt7WQC5Ply63E7WZW/jSM+FA==", "dev": true, "requires": { "cross-spawn": "^5.1.0", @@ -30745,7 +30745,7 @@ "css-color-keywords": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", - "integrity": "sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU=" + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==" }, "css-color-names": { "version": "1.0.1", @@ -30830,7 +30830,7 @@ "css-mediaquery": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/css-mediaquery/-/css-mediaquery-0.1.2.tgz", - "integrity": "sha1-aiw3NEkoYYYxxUvTPO3TAdoYvqA=" + "integrity": "sha512-COtn4EROW5dBGlE/4PiKnh6rZpAPxDeFLaEEwt4i10jpDMFt2EhQGS79QmmrO+iKCHv0PU/HrOWEhijFd1x99Q==" }, "css-to-react-native": { "version": "2.3.2", @@ -30983,7 +30983,7 @@ "currently-unhandled": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "integrity": "sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==", "dev": true, "optional": true, "requires": { @@ -30993,7 +30993,7 @@ "cwd": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/cwd/-/cwd-0.10.0.tgz", - "integrity": "sha1-FyQAaUBXwioTsM8WFix+S3p/5Wc=", + "integrity": "sha512-YGZxdTTL9lmLkCUTpg4j0zQ7IhRB5ZmqNBbGCl3Tg6MP/d5/6sY7L5mmTjzbc6JKgVZYiqTQTNhPFsbXNGlRaA==", "dev": true, "requires": { "find-pkg": "^0.1.2", @@ -31021,7 +31021,7 @@ "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", "dev": true, "requires": { "assert-plus": "^1.0.0" @@ -31117,7 +31117,7 @@ "debuglog": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz", - "integrity": "sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=", + "integrity": "sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw==", "dev": true }, "decache": { @@ -31132,7 +31132,7 @@ "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==" }, "decamelize-keys": { "version": "1.1.0", @@ -31183,7 +31183,7 @@ "dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", "dev": true }, "deep-equal": { @@ -31217,7 +31217,7 @@ "deep-freeze": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/deep-freeze/-/deep-freeze-0.0.1.tgz", - "integrity": "sha1-OgsABd4YZygZ39OM0x+RF5yJPoQ=", + "integrity": "sha512-Z+z8HiAvsGwmjqlphnHW5oz6yWlOwu6EQfFTjmeTWlDeda3FS2yv3jhq35TX/ewmsnqB+RX2IdsIOyjJCQN5tg==", "dev": true }, "deep-is": { @@ -31526,7 +31526,7 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true }, "delegate": { @@ -31537,13 +31537,13 @@ "delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", "dev": true }, "denodeify": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/denodeify/-/denodeify-1.2.1.tgz", - "integrity": "sha1-OjYof1A05pnnV3kBBSwubJQlFjE=" + "integrity": "sha512-KNTihKNmQENUZeKu5fzfpzRqR5S2VMp4gl9RFHiWzj9DfvYQPMJ6XHKNaQxaGCXwPk6y9yme3aUoaiAe+KX+vg==" }, "depd": { "version": "1.1.2", @@ -31589,7 +31589,7 @@ "detect-indent": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-5.0.0.tgz", - "integrity": "sha1-OHHMCmoALow+Wzz38zYmRnXwa50=", + "integrity": "sha512-rlpvsxUtM0PQvy9iZe640/IWwWYyBsTApREbA1pHOpmOUIl9MkP/U4z7vTtg4Oaojvqhxt7sdufnT0EzGaR31g==", "dev": true }, "detect-newline": { @@ -31813,7 +31813,7 @@ "dns-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", + "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==", "dev": true }, "dns-packet": { @@ -31868,7 +31868,7 @@ "dom-scroll-into-view": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/dom-scroll-into-view/-/dom-scroll-into-view-1.2.1.tgz", - "integrity": "sha1-6PNnMt0ImwIBqI14Fdw/iObWbH4=" + "integrity": "sha512-LwNVg3GJOprWDO+QhLL1Z9MMgWe/KAFLxVWKzjRTxNSPn8/LLDIfmuG71YHznXCqaqTjvHJDYO1MEAgX6XCNbQ==" }, "dom-serializer": { "version": "0.1.0", @@ -31978,7 +31978,7 @@ "downloadjs": { "version": "1.4.7", "resolved": "https://registry.npmjs.org/downloadjs/-/downloadjs-1.4.7.tgz", - "integrity": "sha1-9p+W+UDg0FU9rCkROYZaPNAQHjw=" + "integrity": "sha512-LN1gO7+u9xjU5oEScGFKvXhYf7Y/empUIIEAGBs1LzUq/rg5duiDrkuH5A2lQGd5jfMOb9X9usDa2oVXwJ0U/Q==" }, "downshift": { "version": "6.1.0", @@ -32029,12 +32029,12 @@ "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "elegant-spinner": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz", - "integrity": "sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=", + "integrity": "sha512-B+ZM+RXvRqQaAmkMlO/oSe5nMUOaUnyfGYCEHoR8wrXsZR2mA0XVibsxV1bvTwxdRWah1PkQqso2EzhILGHtEQ==", "dev": true }, "element-resize-detector": { @@ -32095,7 +32095,7 @@ "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" }, "encoding": { "version": "0.1.12", @@ -32337,7 +32337,7 @@ "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, "escape-latex": { "version": "1.2.0", @@ -33844,7 +33844,7 @@ "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" }, "event-target-shim": { "version": "5.0.1", @@ -33854,7 +33854,7 @@ "eventemitter2": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-1.0.5.tgz", - "integrity": "sha1-+YNhBRexc3wLncZDvsqTiTwE3xg=" + "integrity": "sha512-EUFhWUYzqqBZlzBMI+dPU8rnKXfQZEUnitnccQuEIAnvWFHCpt3+4fts2+4dpxLtlsiseVXCMFg37KjYChSxpg==" }, "eventemitter3": { "version": "4.0.7", @@ -34011,13 +34011,13 @@ "exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", "dev": true }, "expand-brackets": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==", "requires": { "debug": "^2.3.3", "define-property": "^0.2.5", @@ -34039,7 +34039,7 @@ "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", "requires": { "is-descriptor": "^0.1.0" } @@ -34047,7 +34047,7 @@ "extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", "requires": { "is-extendable": "^0.1.0" } @@ -34057,7 +34057,7 @@ "expand-tilde": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-1.2.2.tgz", - "integrity": "sha1-C4HrqJflo9MdHD0QL48BRB5VlEk=", + "integrity": "sha512-rtmc+cjLZqnu9dSYosX9EWmSJhTwpACgJQTfj4hgg2JjOD/6SIQalZrt4a3aQeh++oNxkazcaxrhPUj6+g5G/Q==", "dev": true, "requires": { "os-homedir": "^1.0.1" @@ -34298,7 +34298,7 @@ "extend-shallow": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", "requires": { "assign-symbols": "^1.0.0", "is-extendable": "^1.0.1" @@ -34364,7 +34364,7 @@ "define-property": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", "requires": { "is-descriptor": "^1.0.0" } @@ -34372,7 +34372,7 @@ "extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", "requires": { "is-extendable": "^0.1.0" } @@ -34431,7 +34431,7 @@ "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", "dev": true }, "fast-average-color": { @@ -34567,7 +34567,7 @@ "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, "fast-memoize": { @@ -34757,7 +34757,7 @@ "filename-reserved-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", - "integrity": "sha1-q/c9+rc10EVECr/qLZHzieu/oik=", + "integrity": "sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==", "dev": true }, "filenamify": { @@ -34800,7 +34800,7 @@ "filter-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", - "integrity": "sha1-mzERErxsYSehbgFsbF1/GeCAXFs=" + "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==" }, "finalhandler": { "version": "1.1.2", @@ -34832,7 +34832,7 @@ "statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==" } } }, @@ -34916,7 +34916,7 @@ "find-file-up": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/find-file-up/-/find-file-up-0.1.3.tgz", - "integrity": "sha1-z2gJG8+fMApA2kEbN9pczlovvqA=", + "integrity": "sha512-mBxmNbVyjg1LQIIpgO8hN+ybWBgDQK8qjht+EbrTCGmmPV/sc7RF1i9stPTD6bpvXZywBdrwRYxhSdJv867L6A==", "dev": true, "requires": { "fs-exists-sync": "^0.1.0", @@ -34932,7 +34932,7 @@ "find-pkg": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/find-pkg/-/find-pkg-0.1.2.tgz", - "integrity": "sha1-G9wiwG42NlUy4qJIBGhUuXiNpVc=", + "integrity": "sha512-0rnQWcFwZr7eO0513HahrWafsc3CTFioEB7DRiEYCUM/70QXSY8f3mCST17HXLcPvEhzH/Ty/Bxd72ZZsr/yvw==", "dev": true, "requires": { "find-file-up": "^0.1.2" @@ -35137,12 +35137,12 @@ "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" + "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==" }, "for-own": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "integrity": "sha512-SKmowqGTJoPzLO1T0BBJpkfp3EMacCMOuH40hOUbrbzElVktk4DioXVM99QkLCyKoiuOmyjgcWMpVz2xjE7LZw==", "dev": true, "requires": { "for-in": "^1.0.1" @@ -35209,7 +35209,7 @@ "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", "dev": true }, "fork-ts-checker-webpack-plugin": { @@ -35356,7 +35356,7 @@ "format": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", - "integrity": "sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs=", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", "dev": true }, "fraction.js": { @@ -35368,7 +35368,7 @@ "fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==", "requires": { "map-cache": "^0.2.2" } @@ -35392,12 +35392,12 @@ "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" }, "from2": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", "dev": true, "requires": { "inherits": "^2.0.1", @@ -35413,7 +35413,7 @@ "fs-exists-sync": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz", - "integrity": "sha1-mC1ok6+RjnLQjeyehnP/K1qNat0=", + "integrity": "sha512-cR/vflFyPZtrN6b38ZyWxpWdhlXrzZEBawlpBQMq7033xVY7/kg0GDMBK5jg8lDYQckdJ5x/YC88lM3C7VMsLg==", "dev": true }, "fs-extra": { @@ -35468,7 +35468,7 @@ "fs-write-stream-atomic": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", - "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "integrity": "sha512-gehEzmPn2nAwr39eay+x3X34Ra+M2QlVUTLhkXPjWdeO8RF9kszk116avgBJM3ZyNHgHXBNx+VmPaFC36k0PzA==", "dev": true, "requires": { "graceful-fs": "^4.1.2", @@ -35480,7 +35480,7 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "fsevents": { "version": "2.3.2", @@ -35833,7 +35833,7 @@ "get-stdin": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "integrity": "sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw==", "dev": true, "optional": true }, @@ -35900,12 +35900,12 @@ "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" + "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==" }, "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", "dev": true, "requires": { "assert-plus": "^1.0.0" @@ -35958,7 +35958,7 @@ "git-remote-origin-url": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz", - "integrity": "sha1-UoJlna4hBxRaERJhEq0yFuxfpl8=", + "integrity": "sha512-eU+GGrZgccNJcsDH5LkXR3PB9M958hxc7sbA8DFJjrv9j4L2P/eZfKhM+QD6wyzpiv+b1BpK0XrYCxkovtjSLw==", "dev": true, "requires": { "gitconfiglocal": "^1.0.0", @@ -36005,7 +36005,7 @@ "gitconfiglocal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz", - "integrity": "sha1-QdBF84UaXqiPA/JMocYXgRRGS5s=", + "integrity": "sha512-spLUXeTAVHxDtKsJc8FkFVgFtMdEN9qPGpL23VfSHx4fP4+Ds097IXLvymbnDH8FnmxX5Nr9bPw3A+AQ6mWEaQ==", "dev": true, "requires": { "ini": "^1.3.2" @@ -36284,13 +36284,13 @@ "globjoin": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", - "integrity": "sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM=", + "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==", "dev": true }, "good-listener": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", - "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=", + "integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==", "requires": { "delegate": "^3.1.2" } @@ -36322,7 +36322,7 @@ "gradient-parser": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/gradient-parser/-/gradient-parser-0.1.5.tgz", - "integrity": "sha1-DH4heVWeXOfY1x9EI6+TcQCyJIw=" + "integrity": "sha512-+uPlcVbjrKOnTzvz0MjTj7BfACj8OmxIa1moIjJV7btvhUMSJk0D47RfDCgDrZE3dYMz9Cf5xKJwnrKLjUq0KQ==" }, "handle-thing": { "version": "2.0.1", @@ -36354,7 +36354,7 @@ "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", "dev": true }, "har-validator": { @@ -36421,7 +36421,7 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" }, "has-glob": { "version": "1.0.0", @@ -36496,13 +36496,13 @@ "has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", "dev": true }, "has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==", "requires": { "get-value": "^2.0.6", "has-values": "^1.0.0", @@ -36512,7 +36512,7 @@ "has-values": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==", "requires": { "is-number": "^3.0.0", "kind-of": "^4.0.0" @@ -36521,7 +36521,7 @@ "kind-of": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==", "requires": { "is-buffer": "^1.1.5" } @@ -36827,7 +36827,7 @@ "hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", "dev": true, "requires": { "hash.js": "^1.0.3", @@ -36881,7 +36881,7 @@ "hpack.js": { "version": "2.1.6", "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", "dev": true, "requires": { "inherits": "^2.0.1", @@ -36987,7 +36987,7 @@ "htmlparser2-without-node-native": { "version": "3.9.2", "resolved": "https://registry.npmjs.org/htmlparser2-without-node-native/-/htmlparser2-without-node-native-3.9.2.tgz", - "integrity": "sha1-s+0FDYd9D/NGWWnjOYd7f59mMfY=", + "integrity": "sha512-+FplQXqmY5fRx6vCIp2P5urWaoBCpTNJMXnKP/6mNCcyb+AZWWJzA8D03peXfozlxDL+vpgLK5dJblqEgu8j6A==", "requires": { "domelementtype": "^1.3.0", "domhandler": "^2.3.0", @@ -37007,7 +37007,7 @@ "http-deceiver": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", "dev": true }, "http-errors": { @@ -37185,7 +37185,7 @@ "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", "dev": true, "requires": { "assert-plus": "^1.0.0", @@ -37214,7 +37214,7 @@ "https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==", "dev": true }, "https-proxy-agent": { @@ -37253,7 +37253,7 @@ "humanize-ms": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", "dev": true, "requires": { "ms": "^2.0.0" @@ -37288,7 +37288,7 @@ "iferr": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", - "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "integrity": "sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA==", "dev": true }, "ignore": { @@ -37406,7 +37406,7 @@ "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==" }, "indent-string": { "version": "3.2.0", @@ -37429,7 +37429,7 @@ "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "requires": { "once": "^1.3.0", "wrappy": "1" @@ -37823,7 +37823,7 @@ "is-accessor-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", "requires": { "kind-of": "^3.0.2" }, @@ -37831,7 +37831,7 @@ "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "requires": { "is-buffer": "^1.1.5" } @@ -37847,7 +37847,7 @@ "is-alphanumeric": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-alphanumeric/-/is-alphanumeric-1.0.0.tgz", - "integrity": "sha1-Spzvcdr0wAHB2B1j0UDPU/1oifQ=", + "integrity": "sha512-ZmRL7++ZkcMOfDuWZuMJyIVLr2keE1o/DeNWh1EmgqGhUcV+9BIVsx0BcSBOHTZqzjs4+dISzr2KAeBEWGgXeA==", "dev": true }, "is-alphanumerical": { @@ -37872,7 +37872,7 @@ "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" }, "is-bigint": { "version": "1.0.1", @@ -37959,7 +37959,7 @@ "is-data-descriptor": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", "requires": { "kind-of": "^3.0.2" }, @@ -37967,7 +37967,7 @@ "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "requires": { "is-buffer": "^1.1.5" } @@ -38006,7 +38006,7 @@ "is-directory": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", - "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=" + "integrity": "sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==" }, "is-docker": { "version": "2.2.1", @@ -38027,12 +38027,12 @@ "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==" }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true }, "is-finite": { @@ -38045,7 +38045,7 @@ "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==" }, "is-function": { "version": "1.0.2", @@ -38219,7 +38219,7 @@ "is-regexp": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", - "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=", + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", "dev": true }, "is-resolvable": { @@ -38252,7 +38252,7 @@ "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==" }, "is-string": { "version": "1.0.5", @@ -38269,7 +38269,7 @@ "is-text-path": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", - "integrity": "sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4=", + "integrity": "sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==", "dev": true, "requires": { "text-extensions": "^1.0.0" @@ -38503,7 +38503,7 @@ "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", "dev": true }, "is-unicode-supported": { @@ -38514,7 +38514,7 @@ "is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==", "dev": true, "optional": true }, @@ -38598,22 +38598,22 @@ "is-wsl": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=" + "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==" }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==" }, "isomorphic-unfetch": { "version": "3.1.0", @@ -38628,7 +38628,7 @@ "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", "dev": true }, "istanbul-lib-coverage": { @@ -38761,7 +38761,7 @@ "jed": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/jed/-/jed-1.1.1.tgz", - "integrity": "sha1-elSbvZ/+FYWwzQoZHiAwVb7ldLQ=" + "integrity": "sha512-z35ZSEcXHxLW4yumw0dF6L464NT36vmx3wxJw8MDpraBcWuNVgUPZgPJKcu1HekNgwlMFNqol7i/IpSbjhqwqA==" }, "jest": { "version": "29.5.0", @@ -39663,7 +39663,7 @@ "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", "dev": true, "optional": true }, @@ -39999,7 +39999,7 @@ "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, "json-stringify-nice": { @@ -40011,7 +40011,7 @@ "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", "dev": true }, "json2php": { @@ -40034,7 +40034,7 @@ "jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "requires": { "graceful-fs": "^4.1.6" } @@ -40042,7 +40042,7 @@ "jsonparse": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", "dev": true }, "jsprim": { @@ -40231,7 +40231,7 @@ "klaw": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", - "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", + "integrity": "sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw==", "requires": { "graceful-fs": "^4.1.9" } @@ -40271,7 +40271,7 @@ "language-tags": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", - "integrity": "sha1-0yHbxNowuovzAk4ED6XBRmH5GTo=", + "integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==", "dev": true, "requires": { "language-subtag-registry": "~0.3.2" @@ -40280,7 +40280,7 @@ "lazy-cache": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", + "integrity": "sha512-RE2g0b5VGZsOCFOCgP7omTRYFqydmZkBwl5oNnQ1lDYC57uyO9KqNnNVxT7COSHTxrRCWVcAVOcbjk+tvh/rgQ==", "dev": true }, "lazy-universal-dotenv": { @@ -40341,7 +40341,7 @@ "levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", "dev": true, "requires": { "prelude-ls": "~1.1.2", @@ -40548,7 +40548,7 @@ "line-height": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/line-height/-/line-height-0.3.1.tgz", - "integrity": "sha1-SxIF7d4YKHKl76PI9iCzGHqcVMk=", + "integrity": "sha512-YExecgqPwnp5gplD2+Y8e8A5+jKpr25+DzMbFdI1/1UAr0FJrTFv4VkHLf8/6B590i1wUPJWMKKldkd/bdQ//w==", "requires": { "computed-style": "~0.1.3" } @@ -40755,13 +40755,13 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true }, "supports-color": { @@ -40923,7 +40923,7 @@ "listr-silent-renderer": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz", - "integrity": "sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4=", + "integrity": "sha512-L26cIFm7/oZeSNVhWB6faeorXhMg4HNlb/dS/7jHhr708jxlXrtrBWo4YUxZQkc6dGoxEAe6J/D3juTRBUzjtA==", "dev": true }, "listr-update-renderer": { @@ -40945,19 +40945,19 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", "dev": true }, "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", "dev": true }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", "dev": true, "requires": { "ansi-styles": "^2.2.1", @@ -40970,7 +40970,7 @@ "figures": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "integrity": "sha512-UxKlfCRuCBxSXU4C6t9scbDyWZ4VlaFFdojKtzJuSkuOBQ5CNFum+zZXFwHjo+CxBC1t6zlYPgHIgFjL8ggoEQ==", "dev": true, "requires": { "escape-string-regexp": "^1.0.5", @@ -40980,7 +40980,7 @@ "log-symbols": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", - "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", + "integrity": "sha512-mmPrW0Fh2fxOzdBbFv4g1m6pR72haFLPJ2G5SJEELf1y+iaQrDG6cWCPjy54RHYbZAt7X+ls690Kw62AdWXBzQ==", "dev": true, "requires": { "chalk": "^1.0.0" @@ -40989,7 +40989,7 @@ "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", "dev": true, "requires": { "ansi-regex": "^2.0.0" @@ -40998,7 +40998,7 @@ "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", "dev": true } } @@ -41046,7 +41046,7 @@ "load-json-file": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "integrity": "sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A==", "dev": true, "optional": true, "requires": { @@ -41060,7 +41060,7 @@ "parse-json": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", "dev": true, "optional": true, "requires": { @@ -41114,53 +41114,53 @@ "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, "lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", "dev": true }, "lodash.difference": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", - "integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=", + "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", "dev": true }, "lodash.flatten": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", "dev": true }, "lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", "dev": true }, "lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" }, "lodash.ismatch": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", - "integrity": "sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc=", + "integrity": "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==", "dev": true }, "lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", "dev": true }, "lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", "dev": true }, "lodash.merge": { @@ -41172,30 +41172,30 @@ "lodash.set": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", - "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=", + "integrity": "sha512-4hNPN5jlm/N/HLMCO43v8BXKq9Z7QdAGc/VGrRD61w8gN9g/6jF9A4L1pbUgBLCffi0w9VsXfTOij5x8iTyFvg==", "dev": true }, "lodash.throttle": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", - "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=" + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==" }, "lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", "dev": true }, "lodash.union": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", - "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=", + "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", "dev": true }, "lodash.uniq": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", "dev": true }, "log-symbols": { @@ -41210,7 +41210,7 @@ "log-update": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/log-update/-/log-update-2.3.0.tgz", - "integrity": "sha1-iDKP19HOeTiykoN0bwsbwSayRwg=", + "integrity": "sha512-vlP11XfFGyeNQlmEn9tJ66rEW1coA/79m5z6BCkudjbAGE83uhAcGYrBFwfs3AdLiLzGRusRPAbSPK9xZteCmg==", "dev": true, "requires": { "ansi-escapes": "^3.0.0", @@ -41242,7 +41242,7 @@ "wrap-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-3.0.1.tgz", - "integrity": "sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo=", + "integrity": "sha512-iXR3tDXpbnTpzjKSylUJRkLuOrEC7hwEB221cgn6wtF8wpmz28puFXAEfPT5zrjM3wahygB//VuWEr1vTkDcNQ==", "dev": true, "requires": { "string-width": "^2.1.1", @@ -41383,7 +41383,7 @@ "loud-rejection": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", - "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "integrity": "sha512-RPNliZOFkqFumDhvYqOaNY4Uz9oJM2K9tC6JWsJJsNdhuONW4LQHRBpb0qf4pJApVffI5N39SwzWZJuEhfd7eQ==", "dev": true, "optional": true, "requires": { @@ -41628,31 +41628,31 @@ "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" + "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==" }, "map-obj": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", "dev": true, "optional": true }, "map-or-similar": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/map-or-similar/-/map-or-similar-1.5.0.tgz", - "integrity": "sha1-beJlMXSt+12e3DPGnT6Sobdvrwg=", + "integrity": "sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg==", "dev": true }, "map-values": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/map-values/-/map-values-1.0.1.tgz", - "integrity": "sha1-douOecAJvytk/ugG4ip7HEGQyZA=", + "integrity": "sha512-BbShUnr5OartXJe1GeccAWtfro11hhgNJg6G9/UtWKjVGvV5U4C09cg5nk8JUevhXODaXY+hQ3xxMUKSs62ONQ==", "dev": true }, "map-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==", "requires": { "object-visit": "^1.0.0" } @@ -42110,7 +42110,7 @@ "mdast-util-inject": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/mdast-util-inject/-/mdast-util-inject-1.1.0.tgz", - "integrity": "sha1-2wa4tYW+lZotzS+H9HK6m3VvNnU=", + "integrity": "sha512-CcJ0mHa36QYumDKiZ2OIR+ClhfOM7zIzN+Wfy8tRZ1hpH9DKLCS+Mh4DyK5bCxzE9uxMWcbIpeNFWsg1zrj/2g==", "dev": true, "requires": { "mdast-util-to-string": "^1.0.0" @@ -42175,13 +42175,13 @@ "mdurl": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", "dev": true }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "dev": true }, "mem": { @@ -42224,7 +42224,7 @@ "memoizerific": { "version": "1.11.3", "resolved": "https://registry.npmjs.org/memoizerific/-/memoizerific-1.11.3.tgz", - "integrity": "sha1-fIekZGREwy11Q4VwkF8tvRsagFo=", + "integrity": "sha512-/EuHYwAPdLtXwAwSZkh/Gutery6pD2KYd44oQLhAvQp/50mpyduZh8Q7PYHXTCJ+wuXxt7oij2LXyIJOOYFPog==", "dev": true, "requires": { "map-or-similar": "^1.5.0" @@ -42233,7 +42233,7 @@ "memory-fs": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "integrity": "sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ==", "dev": true, "requires": { "errno": "^0.1.3", @@ -42243,7 +42243,7 @@ "memorystream": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", - "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", "dev": true }, "meow": { @@ -42380,7 +42380,7 @@ "clone-deep": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-0.2.4.tgz", - "integrity": "sha1-TnPdCen7lxzDhnDF3O2cGJZIHMY=", + "integrity": "sha512-we+NuQo2DHhSl+DP6jlUiAhyAjBQrYnpOk15rN6c6JSPScjiCLh8IbSU+VTcph6YS3o7mASE8a0+gbZ7ChLpgg==", "dev": true, "requires": { "for-own": "^0.1.3", @@ -42404,7 +42404,7 @@ "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "dev": true, "requires": { "is-buffer": "^1.1.5" @@ -42413,7 +42413,7 @@ "shallow-clone": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-0.1.2.tgz", - "integrity": "sha1-WQnodLp3EG1zrEFM/sH/yofZcGA=", + "integrity": "sha512-J1zdXCky5GmNnuauESROVu31MQSnLoYvlyEn6j2Ztk6Q5EHFIhxkMhYcv6vuDzl2XEzoRr856QwzMgWM/TmZgw==", "dev": true, "requires": { "is-extendable": "^0.1.1", @@ -42425,7 +42425,7 @@ "kind-of": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-2.0.1.tgz", - "integrity": "sha1-AY7HpM5+OobLkUG+UZ0kyPqpgbU=", + "integrity": "sha512-0u8i1NZ/mg0b+W3MGGw5I7+6Eib2nx72S/QvXa0hYjEkjTknYmEYQJwGu3mLC0BrhtJjtQafTkyRUQ75Kx0LVg==", "dev": true, "requires": { "is-buffer": "^1.0.2" @@ -42434,7 +42434,7 @@ "lazy-cache": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-0.2.7.tgz", - "integrity": "sha1-f+3fLctu23fRHvHRF6tf/fCrG2U=", + "integrity": "sha512-gkX52wvU/R8DVMMt78ATVPFMJqfW8FPz1GZ1sVHBVQHmu/WvhIWE4cE1GBzhJNFicDeYhnwp6Rl35BcAIM3YOQ==", "dev": true } } @@ -42444,7 +42444,7 @@ "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", "dev": true }, "merge-stream": { @@ -42461,7 +42461,7 @@ "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "dev": true }, "metro": { @@ -43604,7 +43604,7 @@ "min-document": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", - "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", + "integrity": "sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==", "dev": true, "requires": { "dom-walk": "^0.1.0" @@ -43681,7 +43681,7 @@ "minimalistic-crypto-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", "dev": true }, "minimatch": { @@ -43943,7 +43943,7 @@ "mixin-object": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz", - "integrity": "sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=", + "integrity": "sha512-ALGF1Jt9ouehcaXaHhn6t1yGWRqGaHkPFndtFVHfZXOvkIZ/yoGaSi0AHVTafb3ZBGg4dr/bDwnaEKqCXzchMA==", "dev": true, "requires": { "for-in": "^0.1.3", @@ -43953,7 +43953,7 @@ "for-in": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz", - "integrity": "sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE=", + "integrity": "sha512-F0to7vbBSHP8E3l6dCjxNOLuSFAACIxFy3UehTUlG7svlXi37HHsDkyVcHo0Pq8QwrE+pXvWSVX3ZT1T9wAZ9g==", "dev": true } } @@ -43961,7 +43961,7 @@ "mkdirp": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "integrity": "sha512-SknJC52obPfGQPnjIkXbmA6+5H15E+fR+E4iR2oQ3zzCLbd7/ONua69R/Gw7AgkTLsRG+r5fzksYwWe1AgTyWA==", "requires": { "minimist": "0.0.8" }, @@ -43969,7 +43969,7 @@ "minimist": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + "integrity": "sha512-miQKw5Hv4NS1Psg2517mV4e4dYNaO3++hjAvLOAzKqZ61rH8NS1SK+vbfBWZ5PY/Me/bEWhUwqMghEW5Fb9T7Q==" } } }, @@ -44040,7 +44040,7 @@ "move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", - "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "integrity": "sha512-hdrFxZOycD/g6A6SoI2bB5NA/5NEqD0569+S47WZhPvm46sD50ZHdYaFmnua5lndde9rCHGjmfK7Z8BuCt/PcQ==", "dev": true, "requires": { "aproba": "^1.1.1", @@ -44171,7 +44171,7 @@ "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, "negotiator": { @@ -44247,7 +44247,7 @@ "node-dir": { "version": "0.1.17", "resolved": "https://registry.npmjs.org/node-dir/-/node-dir-0.1.17.tgz", - "integrity": "sha1-X1Zl2TNRM1yqvvjxxVRRbPXx5OU=", + "integrity": "sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg==", "requires": { "minimatch": "^3.0.2" } @@ -44315,7 +44315,7 @@ "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=" + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==" }, "node-libs-browser": { "version": "2.2.1", @@ -44351,7 +44351,7 @@ "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", "dev": true } } @@ -44414,7 +44414,7 @@ "normalize-range": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", "dev": true }, "normalize-selector": { @@ -44999,7 +44999,7 @@ "load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", "dev": true, "requires": { "graceful-fs": "^4.1.2", @@ -45011,7 +45011,7 @@ "parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", "dev": true, "requires": { "error-ex": "^1.3.1", @@ -45021,13 +45021,13 @@ "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", "dev": true }, "read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", "dev": true, "requires": { "load-json-file": "^4.0.0", @@ -45044,7 +45044,7 @@ "strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true } } @@ -45052,7 +45052,7 @@ "npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", "requires": { "path-key": "^2.0.0" } @@ -45083,7 +45083,7 @@ "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", "dev": true }, "nwsapi": { @@ -45495,12 +45495,12 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" }, "object-copy": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==", "requires": { "copy-descriptor": "^0.1.0", "define-property": "^0.2.5", @@ -45510,7 +45510,7 @@ "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", "requires": { "is-descriptor": "^0.1.0" } @@ -45518,7 +45518,7 @@ "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "requires": { "is-buffer": "^1.1.5" } @@ -45528,7 +45528,7 @@ "object-filter": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/object-filter/-/object-filter-1.0.2.tgz", - "integrity": "sha1-rwt5f/6+r4pSxmN87b6IFs/sG8g=", + "integrity": "sha512-NahvP2vZcy1ZiiYah30CEPw0FpDcSkSePJBMpzl5EQgCmISijiGuJm3SPYp7U+Lf2TljyaIw3E5EgkEx/TNEVA==", "dev": true }, "object-inspect": { @@ -45576,7 +45576,7 @@ "object-visit": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==", "requires": { "isobject": "^3.0.0" } @@ -46179,7 +46179,7 @@ "object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", "requires": { "isobject": "^3.0.1" } @@ -46358,7 +46358,7 @@ "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "requires": { "wrappy": "1" } @@ -46498,13 +46498,13 @@ "os-browserify": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", "dev": true }, "os-homedir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", "dev": true }, "os-name": { @@ -46520,7 +46520,7 @@ "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==" }, "p-all": { "version": "2.1.0", @@ -46580,7 +46580,7 @@ "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==" }, "p-limit": { "version": "3.1.0", @@ -47016,7 +47016,7 @@ "parse-passwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", "dev": true }, "parse-path": { @@ -47071,7 +47071,7 @@ "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" + "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==" }, "patch-package": { "version": "6.2.2", @@ -47192,29 +47192,29 @@ "path-dirname": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==", "dev": true }, "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==" }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" }, "path-is-inside": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", "dev": true }, "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==" }, "path-parse": { "version": "1.0.7", @@ -47224,7 +47224,7 @@ "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", "dev": true }, "path-type": { @@ -47260,24 +47260,24 @@ "pegjs": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/pegjs/-/pegjs-0.10.0.tgz", - "integrity": "sha1-z4uvrm7d/0tafvsYUmnqr0YQ3b0=" + "integrity": "sha512-qI5+oFNEGi3L5HAxDwN2LA4Gg7irF70Zs25edhjld9QemOgp0CbvMtbFcMvFtEo1OityPrcCzkQFB8JP/hxgow==" }, "pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "dev": true }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", "dev": true }, "phpegjs": { "version": "1.0.0-beta7", "resolved": "https://registry.npmjs.org/phpegjs/-/phpegjs-1.0.0-beta7.tgz", - "integrity": "sha1-uLbthQGYB//Q7+ID4AKj5e2LTZQ=" + "integrity": "sha512-SO+NP5argMoJVCWcYiOofPUeEWDIM47FNCBJtp6uJ8PpjtBcudYJTzCbCMit5dzmfSLCoijzEwIXOqPqD45xQg==" }, "picocolors": { "version": "1.0.0", @@ -47304,13 +47304,13 @@ "pinkie": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", "dev": true }, "pinkie-promise": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", "dev": true, "requires": { "pinkie": "^2.0.0" @@ -47428,7 +47428,7 @@ "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" + "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==" }, "postcss": { "version": "8.4.16", @@ -47589,7 +47589,7 @@ "postcss-media-query-parser": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", - "integrity": "sha1-J7Ocb02U+Bsac7j3Y1HGCeXO8kQ=", + "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==", "dev": true }, "postcss-merge-longhand": { @@ -47961,7 +47961,7 @@ "postcss-resolve-nested-selector": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz", - "integrity": "sha1-Kcy8fDfe36wwTp//C/FZaz9qDk4=", + "integrity": "sha512-HvExULSwLqHLgUy1rl3ANIqCsvMS0WHss2UOsXhXnQaZ9VCc2oBvIpXrl00IUFT5ZDITME0o6oiXeiHr2SAIfw==", "dev": true }, "postcss-safe-parser": { @@ -48036,7 +48036,7 @@ "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", "dev": true }, "prettier": { @@ -48110,7 +48110,7 @@ "process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", "dev": true }, "process-nextick-args": { @@ -48147,7 +48147,7 @@ "promise-inflight": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", "dev": true }, "promise-retry": { @@ -48651,7 +48651,7 @@ "promzard": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/promzard/-/promzard-0.3.0.tgz", - "integrity": "sha1-JqXW7ox97kyxIggwWs+5O6OCqe4=", + "integrity": "sha512-JZeYqd7UAcHCwI+sTOeUDYkvEU+1bQ7iE0UT1MgB/tERkAPkesW46MrpIySzODi+owTjZtiF8Ay5j9m60KmMBw==", "dev": true, "requires": { "read": "1" @@ -48685,7 +48685,7 @@ "proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", "dev": true }, "protocols": { @@ -48708,13 +48708,13 @@ "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", "dev": true }, "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", "dev": true }, "psl": { @@ -49070,7 +49070,7 @@ "q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", "dev": true }, "qs": { @@ -49101,7 +49101,7 @@ "querystring-es3": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==", "dev": true }, "querystringify": { @@ -50302,7 +50302,7 @@ "read": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", - "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", + "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==", "dev": true, "requires": { "mute-stream": "~0.0.4" @@ -50581,7 +50581,7 @@ "readline": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/readline/-/readline-1.3.0.tgz", - "integrity": "sha1-xYDXfvLPyHUrEySYBg3JeTp6wBw=" + "integrity": "sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg==" }, "reakit": { "version": "1.3.11", @@ -50648,7 +50648,7 @@ "redent": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", - "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "integrity": "sha512-qtW5hKzGQZqKoh6JNSD+4lfitfPKGz42e6QwiRmPM5mmKtR0N41AbJRYu0xJi7nhOJ4WDgRkKvAk6tw4WIwR4g==", "dev": true, "optional": true, "requires": { @@ -50659,7 +50659,7 @@ "indent-string": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", - "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "integrity": "sha512-aqwDFWSgSgfRaEwao5lg5KEcVd/2a+D1rvoG7NdilmYz0NwRk6StWpWdz/Hpk34MKPpx7s8XxUqimfcQK6gGlg==", "dev": true, "optional": true, "requires": { @@ -51286,7 +51286,7 @@ "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", "dev": true }, "renderkid": { @@ -51410,12 +51410,12 @@ "repeat-string": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==" }, "repeating": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "integrity": "sha512-ZqtSMuVybkISo2OWvqvm7iHSWngvdaW3IpsT9/uP8v4gMi591LY6h35wdOfvQdWCKFWZWm2Y1Opp4kV7vQKT6A==", "dev": true, "optional": true, "requires": { @@ -51425,7 +51425,7 @@ "replace-ext": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", - "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "integrity": "sha512-vuNYXC7gG7IeVNBC1xUllqCcZKRbJoSPOBhnTEcAIiKCsbuef6zO3F0Rve3isPMMoNoQRWjQwbAgAjHUHniyEA==", "dev": true }, "request": { @@ -51504,7 +51504,7 @@ "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" }, "require-from-string": { "version": "2.0.2", @@ -51526,7 +51526,7 @@ "requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "dev": true }, "resize-observer-polyfill": { @@ -51592,7 +51592,7 @@ "resolve-dir": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-0.1.1.tgz", - "integrity": "sha1-shklmlYC+sXFxJatiUpujMQwJh4=", + "integrity": "sha512-QxMPqI6le2u0dCLyiGzgy92kjkkL6zO0XyvHzjdTNH3zM6e5Hz3BwG6+aEyNgiQ5Xz6PwTwgQEj3U50dByPKIA==", "dev": true, "requires": { "expand-tilde": "^1.2.2", @@ -51637,7 +51637,7 @@ "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" + "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==" }, "resolve.exports": { "version": "2.0.1", @@ -51801,7 +51801,7 @@ "run-queue": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", - "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "integrity": "sha512-ntymy489o0/QQplUDnpYAYUsO50K9SBrIVaKCWDOJzYJts0f9WH9RFJkyagebkw5+y1oi00R7ynNW/d12GBumg==", "dev": true, "requires": { "aproba": "^1.1.1" @@ -51810,12 +51810,12 @@ "rungen": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/rungen/-/rungen-0.3.2.tgz", - "integrity": "sha1-QAwJ6+kU57F+C27zJjQA/Cq8fLM=" + "integrity": "sha512-zWl10xu2D7zoR8zSC2U6bg5bYF6T/Wk7rxwp8IPaJH7f0Ge21G03kNHVgHR7tyVkSSfAOG0Rqf/Cl38JftSmtw==" }, "rx": { "version": "2.3.24", "resolved": "https://registry.npmjs.org/rx/-/rx-2.3.24.tgz", - "integrity": "sha1-FPlQpCF9fjXapxu8vljv9o6ksrc=", + "integrity": "sha512-Ue4ZB7Dzbn2I9sIj8ws536nOP2S53uypyCkCz9q0vlYD5Kn6/pu4dE+wt2ZfFzd9m73hiYKnnCb1OyKqc+MRkg==", "dev": true }, "rxjs": { @@ -51843,7 +51843,7 @@ "safe-regex": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==", "requires": { "ret": "~0.1.10" } @@ -52245,12 +52245,12 @@ "select": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", - "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=" + "integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA==" }, "select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", "dev": true }, "selfsigned": { @@ -52291,7 +52291,7 @@ "semver-compare": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", "dev": true }, "send": { @@ -52377,7 +52377,7 @@ "serialize-error": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz", - "integrity": "sha1-ULZ51WNc34Rme9yOWa9OW4HV9go=" + "integrity": "sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw==" }, "serialize-javascript": { "version": "4.0.0", @@ -52391,7 +52391,7 @@ "serve-favicon": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.5.0.tgz", - "integrity": "sha1-k10kDN/g9YBTB/3+ln2IlCosvPA=", + "integrity": "sha512-FMW2RvqNr03x+C0WxTyu6sOv21oOjkq5j8tjquWccwa6ScNyGFOGJVpuS1NmTVGBAHS07xnSKotgf2ehQmf9iA==", "dev": true, "requires": { "etag": "~1.8.1", @@ -52418,7 +52418,7 @@ "serve-index": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", "dev": true, "requires": { "accepts": "~1.3.4", @@ -52442,7 +52442,7 @@ "http-errors": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", "dev": true, "requires": { "depd": "~1.1.2", @@ -52473,7 +52473,7 @@ "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" }, "set-value": { "version": "2.0.1", @@ -52489,7 +52489,7 @@ "extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", "requires": { "is-extendable": "^0.1.0" } @@ -52507,7 +52507,7 @@ "setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" }, "setprototypeof": { "version": "1.2.0", @@ -52541,7 +52541,7 @@ "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", "requires": { "shebang-regex": "^1.0.0" } @@ -52549,7 +52549,7 @@ "shebang-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==" }, "shell-quote": { "version": "1.7.3", @@ -52749,7 +52749,7 @@ "simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", "requires": { "is-arrayish": "^0.3.1" }, @@ -52837,7 +52837,7 @@ "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", "requires": { "is-descriptor": "^0.1.0" } @@ -52845,7 +52845,7 @@ "extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", "requires": { "is-extendable": "^0.1.0" } @@ -52870,7 +52870,7 @@ "define-property": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", "requires": { "is-descriptor": "^1.0.0" } @@ -52914,7 +52914,7 @@ "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "requires": { "is-buffer": "^1.1.5" } @@ -53012,7 +53012,7 @@ "sort-keys": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz", - "integrity": "sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg=", + "integrity": "sha512-/dPCrG1s3ePpWm6yBbxZq5Be1dXGLyLn9Z791chDC3NFrpkVbWGzkBwPN1knaciexFXgRJ7hzdnwZ4stHSDmjg==", "dev": true, "requires": { "is-plain-obj": "^1.0.0" @@ -53089,7 +53089,7 @@ "spawn-command": { "version": "0.0.2-1", "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", - "integrity": "sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=", + "integrity": "sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==", "dev": true }, "spawnd": { @@ -53268,7 +53268,7 @@ "sprintf-js": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.1.tgz", - "integrity": "sha1-Nr54Mgr+WAH2zqPueLblqrlA6gw=" + "integrity": "sha512-h/U+VScR2Ft+aXDjGTLtguUEIrYuOjTj79BAOElUvdahYMaaa7SNLjJpOIn+Uzt0hsgHfYvlbcno3e9yXOSo8Q==" }, "sshpk": { "version": "1.14.2", @@ -53348,7 +53348,7 @@ "static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==", "requires": { "define-property": "^0.2.5", "object-copy": "^0.1.0" @@ -53357,7 +53357,7 @@ "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", "requires": { "is-descriptor": "^0.1.0" } @@ -53424,7 +53424,7 @@ "strict-uri-encode": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", - "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=" + "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==" }, "string-hash-64": { "version": "1.0.3", @@ -54435,7 +54435,7 @@ "strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" + "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==" }, "strip-final-newline": { "version": "2.0.0", @@ -54446,7 +54446,7 @@ "strip-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", - "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "integrity": "sha512-I5iQq6aFMM62fBEAIB/hXzwJD6EEZ0xEGCX2t7oXqaKPIRgt4WruAQ285BISgdkP+HLGWyeGmNJcpIwFeRYRUA==", "dev": true, "optional": true, "requires": { @@ -54488,7 +54488,7 @@ "style-search": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz", - "integrity": "sha1-eVjHk+R+MuB9K1yv5cC/jhLneQI=", + "integrity": "sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==", "dev": true }, "style-to-object": { @@ -55105,7 +55105,7 @@ "svg-tags": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", - "integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=", + "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", "dev": true }, "svgo": { @@ -55676,7 +55676,7 @@ "temp": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz", - "integrity": "sha1-4Ma8TSa5AxJEEOT+2BEDAU38H1k=", + "integrity": "sha512-jtnWJs6B1cZlHs9wPG7BrowKxZw/rf6+UpGAkr8AaYmiTyTO7zQlLoST8zx/8TcUPnZmeBoB+H8ARuHZaSijVw==", "requires": { "os-tmpdir": "^1.0.0", "rimraf": "~2.2.6" @@ -55685,14 +55685,14 @@ "rimraf": { "version": "2.2.8", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", - "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=" + "integrity": "sha512-R5KMKHnPAQaZMqLOsyuyUmcIjSeDm+73eoqQpaXA7AZ22BL+6C+1mcUscgOsNd8WVlJuvlgAPsegcx7pjlV0Dg==" } } }, "temp-dir": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz", - "integrity": "sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0=", + "integrity": "sha512-xZFXEGbG7SNC3itwBzI3RYjq/cEhBkx2hJuKGIUOcEULmkQExXiHat2z/qkISYsuR+IKumhEfKKbV5qXmhICFQ==", "dev": true }, "terminal-link": { @@ -55859,7 +55859,7 @@ "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, "throat": { @@ -55876,7 +55876,7 @@ "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, "through2": { @@ -55936,18 +55936,18 @@ "to-arraybuffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", - "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "integrity": "sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==", "dev": true }, "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==" }, "to-object-path": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==", "requires": { "kind-of": "^3.0.2" }, @@ -55955,7 +55955,7 @@ "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "requires": { "is-buffer": "^1.1.5" } @@ -56032,20 +56032,20 @@ "trim": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", - "integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=", + "integrity": "sha512-YzQV+TZg4AxpKxaTHK3c3D+kRDCGVEE7LemdlQZoQXn0iennk10RsIoY6ikzAqJTc9Xjl9C1/waHom/J86ziAQ==", "dev": true }, "trim-newlines": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", - "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "integrity": "sha512-Nm4cF79FhSTzrLKGDMi3I4utBtFv8qKy4sq1enftf2gMdpqI8oVQTAfySkTz5r49giVzDj88SVZXP4CeYQwjaw==", "dev": true, "optional": true }, "trim-repeated": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", - "integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=", + "integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==", "dev": true, "requires": { "escape-string-regexp": "^1.0.2" @@ -56129,7 +56129,7 @@ "tty-browserify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", - "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "integrity": "sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw==", "dev": true }, "tunnel": { @@ -56141,7 +56141,7 @@ "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", "dev": true, "requires": { "safe-buffer": "^5.0.1" @@ -56155,14 +56155,14 @@ "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", "dev": true, "optional": true }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", "dev": true, "requires": { "prelude-ls": "~1.1.2" @@ -56216,7 +56216,7 @@ "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", "dev": true }, "typedarray-to-buffer": { @@ -56526,12 +56526,12 @@ "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" }, "unset-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==", "requires": { "has-value": "^0.3.1", "isobject": "^3.0.0" @@ -56540,7 +56540,7 @@ "has-value": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==", "requires": { "get-value": "^2.0.3", "has-values": "^0.1.4", @@ -56550,7 +56550,7 @@ "isobject": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", "requires": { "isarray": "1.0.0" } @@ -56560,14 +56560,14 @@ "has-values": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" + "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==" } } }, "untildify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/untildify/-/untildify-2.1.0.tgz", - "integrity": "sha1-F+soB5h/dpUunASF/DEdBqgmouA=", + "integrity": "sha512-sJjbDp2GodvkB0FZZcn7k6afVisqX5BZD7Yq3xp4nN2O15BBK0cLm3Vwn2vQaF7UDS0UUsrQMkkplmDI5fskig==", "dev": true, "optional": true, "requires": { @@ -56609,7 +56609,7 @@ "urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" + "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==" }, "url": { "version": "0.11.0", @@ -56721,7 +56721,7 @@ "url-template": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", - "integrity": "sha1-/FZaPMy/93MMd19WQflVV5FDnyE=", + "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==", "dev": true }, "use": { @@ -56796,7 +56796,7 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "util.promisify": { "version": "1.0.0", @@ -56811,7 +56811,7 @@ "utila": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==", "dev": true }, "utility-types": { @@ -56822,7 +56822,7 @@ "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" }, "uuid": { "version": "8.3.0", @@ -56832,7 +56832,7 @@ "uuid-browser": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/uuid-browser/-/uuid-browser-3.1.0.tgz", - "integrity": "sha1-DwWkCu90+eWVHiDvv0SxGHHlZBA=", + "integrity": "sha512-dsNgbLaTrd6l3MMxTtouOCFw4CBFc/3a+GgYA2YyrJvyQ1u6q4pcu3ktLoUZ/VN/Aw9WsauazbgsgdfVWgAKQg==", "dev": true }, "v8-compile-cache": { @@ -56873,7 +56873,7 @@ "validate-npm-package-name": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", - "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=", + "integrity": "sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==", "dev": true, "requires": { "builtins": "^1.0.3" @@ -56891,13 +56891,13 @@ "vargs": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/vargs/-/vargs-0.1.0.tgz", - "integrity": "sha1-a2GE2mUgzDIEzhtAfKwm2SYJ6/8=", + "integrity": "sha512-d/j1kMUt0YjLCQPAI+VMZ7IKwNGjk8dSHMCrHq9txFOCcCIDoe8ck9FmPvABJgxIaZO1tabXmNojQG6mBkLLCw==", "dev": true }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" }, "vendors": { "version": "1.0.4", @@ -56908,7 +56908,7 @@ "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", "dev": true, "requires": { "assert-plus": "^1.0.0", @@ -56919,7 +56919,7 @@ "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", "dev": true } } @@ -57175,7 +57175,7 @@ "is-binary-path": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", "dev": true, "optional": true, "requires": { @@ -57237,7 +57237,7 @@ "wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", "requires": { "defaults": "^1.0.3" } @@ -58818,7 +58818,7 @@ "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", "dev": true }, "worker-farm": { @@ -58908,7 +58908,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "write-file-atomic": { "version": "2.4.3", @@ -58991,7 +58991,7 @@ "x-default-browser": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/x-default-browser/-/x-default-browser-0.4.0.tgz", - "integrity": "sha1-cM8NqF2nwKtcsPFaiX8jIqa91IE=", + "integrity": "sha512-7LKo7RtWfoFN/rHx1UELv/2zHGMx8MkZKDq1xENmOCTkfIqZJ0zZ26NEJX8czhnPXVcqS0ARjjfJB+eJ0/5Cvw==", "dev": true, "requires": { "default-browser-id": "^1.0.4" @@ -59000,13 +59000,13 @@ "x-is-string": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/x-is-string/-/x-is-string-0.1.0.tgz", - "integrity": "sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI=", + "integrity": "sha512-GojqklwG8gpzOVEVki5KudKNoq7MbbjYZCbyWzEz7tyPA7eleiE0+ePwOWQQRb5fm86rD3S8Tc0tSFf3AOv50w==", "dev": true }, "xml": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", - "integrity": "sha1-eLpyAgApxbyHuKgaPPzXS0ovweU=", + "integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==", "dev": true }, "xml-name-validator": { diff --git a/packages/components/package.json b/packages/components/package.json index 7e4925bd4fcc63..71f01450f12071 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -66,7 +66,7 @@ "dom-scroll-into-view": "^1.2.1", "downshift": "^6.0.15", "fast-deep-equal": "^3.1.3", - "framer-motion": "^10.11.6", + "framer-motion": "~10.11.6", "gradient-parser": "^0.1.5", "highlight-words-core": "^1.2.2", "is-plain-object": "^5.0.0", From aaf84f382c1339cc5c473a7771588d72e4d413ba Mon Sep 17 00:00:00 2001 From: Gutenberg Repository Automation Date: Tue, 27 Jun 2023 01:44:26 +0000 Subject: [PATCH 021/266] Bump plugin version to 16.1.0-rc.2 --- gutenberg.php | 2 +- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gutenberg.php b/gutenberg.php index bbc497c60cd0a0..abeab01e72976f 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.1 * Requires PHP: 5.6 - * Version: 16.1.0-rc.1 + * Version: 16.1.0-rc.2 * Author: Gutenberg Team * Text Domain: gutenberg * diff --git a/package-lock.json b/package-lock.json index 946b738688b0b8..07bfd23f236659 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "16.1.0-rc.1", + "version": "16.1.0-rc.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 8808dcc7370b85..a061969e8817e9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "16.1.0-rc.1", + "version": "16.1.0-rc.2", "private": true, "description": "A new WordPress editor experience.", "author": "The WordPress Contributors", From 40aa28015c2ddb692d9d1dd118e4c56b56514bd1 Mon Sep 17 00:00:00 2001 From: Robert Anderson Date: Tue, 27 Jun 2023 15:01:58 +1000 Subject: [PATCH 022/266] Fix phpunit failures (#51950) * Fix phpunit failures * Add comment * Update comment with actual reason this fix works --- phpunit/block-template-utils-test.php | 6 ++---- phpunit/bootstrap.php | 6 ++++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/phpunit/block-template-utils-test.php b/phpunit/block-template-utils-test.php index ac2e4145cd0ed2..08fb0c2c2c1e3b 100644 --- a/phpunit/block-template-utils-test.php +++ b/phpunit/block-template-utils-test.php @@ -9,9 +9,6 @@ class Tests_Block_Template_Utils extends WP_UnitTestCase { public function set_up() { parent::set_up(); switch_theme( 'emptytheme' ); - } - - public static function wpSetUpBeforeClass() { register_post_type( 'custom_book', array( @@ -22,9 +19,10 @@ public static function wpSetUpBeforeClass() { register_taxonomy( 'book_type', 'custom_book' ); } - public static function wpTearDownAfterClass() { + public function tear_down() { unregister_post_type( 'custom_book' ); unregister_taxonomy( 'book_type' ); + parent::tear_down(); } public function test_get_template_hierarchy() { diff --git a/phpunit/bootstrap.php b/phpunit/bootstrap.php index c11233b1e89525..f64e6fa5c7ef14 100644 --- a/phpunit/bootstrap.php +++ b/phpunit/bootstrap.php @@ -22,6 +22,12 @@ define( 'LOCAL_WP_ENVIRONMENT_TYPE', 'local' ); } +// Pretend that these are Core unit tests. This is needed so that +// wp_theme_has_theme_json() does not cache its return value between each test. +if ( ! defined( 'WP_RUN_CORE_TESTS' ) ) { + define( 'WP_RUN_CORE_TESTS', true ); +} + // Require composer dependencies. require_once dirname( __DIR__ ) . '/vendor/autoload.php'; From 636164df23ac02d25c603ad51897f0327b762aaf Mon Sep 17 00:00:00 2001 From: Gutenberg Repository Automation Date: Tue, 27 Jun 2023 07:38:23 +0000 Subject: [PATCH 023/266] Update Changelog for 16.1.0-rc.2 --- changelog.txt | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/changelog.txt b/changelog.txt index 927284534179f4..b392837ad2d2cd 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,76 @@ == Changelog == += 16.1.0-rc.2 = + +## Changelog + +### Enhancements + +#### Block Editor +- Improve LinkControl Edit UI. ([51712](https://github.com/WordPress/gutenberg/pull/51712)) + +#### Site Editor +- Adding the distraction-free mode to the site editor (#51173). ([51932](https://github.com/WordPress/gutenberg/pull/51932)) +- Update colors in 'Site view'. ([51856](https://github.com/WordPress/gutenberg/pull/51856)) +- Site Editor: Make string to add Template parts and Patterns consistent and translatable [51743](https://github.com/WordPress/gutenberg/pull/51743) + +#### Block Library +- Add image block aspect ratio control [51545](https://github.com/WordPress/gutenberg/pull/51545) + +#### Components +- Button: Introduce size prop [51842](https://github.com/WordPress/gutenberg/pull/51842) + +#### Navigation +- Ensure there is always a Navigation available in the browse mode sidebar via fallback algorithm [50321](https://github.com/WordPress/gutenberg/pull/50321) + +### Accessibility + +- BlockLockModal: restore focus on fallback toolbar button when original button is not rendered [51666](https://github.com/WordPress/gutenberg/pull/51666) +- Site Editor Sidebar: improvements to buttons [51762](https://github.com/WordPress/gutenberg/pull/51762) + +### Bug fixes + +#### Site Editor +- Disable the revision button if there is no clickable menu [51851](https://github.com/WordPress/gutenberg/pull/51851) +- Library: Fix misalignment of description in custom template parts [51868](https://github.com/WordPress/gutenberg/pull/51868) +- Library: Fix delete shortcut incorrectly bound to non-user patterns [51830](https://github.com/WordPress/gutenberg/pull/51830) +- Add distraction free to site editor +[51173](https://github.com/WordPress/gutenberg/pull/51173) +- Fix toolbar overlap in site editor +[51810](https://github.com/WordPress/gutenberg/pull/51810) +- Increase space between page meta and details section [51858](https://github.com/WordPress/gutenberg/pull/51858/) +- Fix missing MenuGroup segment in Site Editor header more menu [51860](https://github.com/WordPress/gutenberg/pull/51860) +- Update the add template modal design [51806](https://github.com/WordPress/gutenberg/pull/51806) +- Update text color of site editor menu item hover/active states [51847](https://github.com/WordPress/gutenberg/pull/51847) + +#### Block Library +- Buttons Block: add support for orientation-based block movers [51831](https://github.com/WordPress/gutenberg/pull/51831) + +#### Command Center +- Add manage all custom patterns command [51845](https://github.com/WordPress/gutenberg/pull/51845) +- Add another batch of commands to the site editor [51832](https://github.com/WordPress/gutenberg/pull/51832) +- Add UI commands to the post editor [51900](https://github.com/WordPress/gutenberg/pull/51900) + +#### Components +- Keep framer-motion from updating minor version [51894](https://github.com/WordPress/gutenberg/pull/51894) +- ConfirmDialog: Fix affirmative action being triggered an extra time when selecting a button via keyboard [51730](https://github.com/WordPress/gutenberg/pull/51730) +- Tweak more icons for high-resolution devices [51768](https://github.com/WordPress/gutenberg/pull/51768) +- ZStack: fix component bounding box to match children [51836](https://github.com/WordPress/gutenberg/pull/51836) + +#### Page Content Focus +- Switch to Page panel when deselecting a block [51881](https://github.com/WordPress/gutenberg/pull/51881) +- Don't show 'Back to page' notification when navigating away from page [51880](https://github.com/WordPress/gutenberg/pull/51880) +- useBlockSync(): Reset inner blocks when component unmounts [51783](https://github.com/WordPress/gutenberg/pull/51783) +- Fix black pixel appearing when block toolbar is empty [51779](https://github.com/WordPress/gutenberg/pull/51779) +- Only show Page Content Focus commands when in edit mode [51888](https://github.com/WordPress/gutenberg/pull/51888) + +## Contributors + +The following contributors merged PRs in this release: + +@jameskoster @priethor @richtabor @t-hamano @draganescu @noisysocks @tellthemachines @ciampo @ntsekouras @youknowriad @talldan @getdave @ajlende @mirka + + = 16.1.0-rc.1 = ## Changelog From 86b4800520ae646b9fc047b85f328519bff34e34 Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Tue, 27 Jun 2023 17:58:00 +0900 Subject: [PATCH 024/266] Modal: Add small top padding to the content so that avoid cutting off the visible outline when hovering items (#51829) * Site Editor: Fix focus cutoff in add template modal * Add padding-top to the modal content * Remove unnecessary padding-top * Remove unnecessary padding-top * Update changelog * Revert top padding from block patterns list * Revert top padding from block patterns list * Remove unnecessary changes * Remove unnecessary changes * Add CSS inline comment * Change padding metrics --- packages/components/CHANGELOG.md | 1 + packages/components/src/modal/style.scss | 5 +++-- .../edit-post/src/components/start-page-options/style.scss | 3 --- .../sidebar-navigation-screen-navigation-menu/style.scss | 5 ----- .../src/components/start-template-options/style.scss | 3 --- 5 files changed, 4 insertions(+), 13 deletions(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 01db98978c1088..6409ce7515646f 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -18,6 +18,7 @@ - `Autocomplete`: Announce how many results are available to screen readers when suggestions list first renders ([#51018](https://github.com/WordPress/gutenberg/pull/51018)). - `ConfirmDialog`: Ensure onConfirm isn't called an extra time when submitting one of the buttons using the keyboard ([#51730](https://github.com/WordPress/gutenberg/pull/51730)). - `ZStack`: ZStack: fix component bounding box to match children ([#51836](https://github.com/WordPress/gutenberg/pull/51836)). +- `Modal`: Add small top padding to the content so that avoid cutting off the visible outline when hovering items ([#51829](https://github.com/WordPress/gutenberg/pull/51829)). ### Internal diff --git a/packages/components/src/modal/style.scss b/packages/components/src/modal/style.scss index 4c210022b8bb6c..d4ca7c311fcf19 100644 --- a/packages/components/src/modal/style.scss +++ b/packages/components/src/modal/style.scss @@ -72,7 +72,7 @@ .components-modal__header { box-sizing: border-box; border-bottom: $border-width solid transparent; - padding: $grid-unit-30 $grid-unit-40 $grid-unit-15; + padding: $grid-unit-30 $grid-unit-40 $grid-unit-10; display: flex; flex-direction: row; justify-content: space-between; @@ -130,7 +130,8 @@ .components-modal__content { flex: 1; margin-top: $header-height + $grid-unit-15; - padding: 0 $grid-unit-40 $grid-unit-40; + // Small top padding required to avoid cutting off the visible outline when the first child element is focusable. + padding: $grid-unit-05 $grid-unit-40 $grid-unit-40; overflow: auto; &.hide-header { diff --git a/packages/edit-post/src/components/start-page-options/style.scss b/packages/edit-post/src/components/start-page-options/style.scss index 0e26c93e386374..52f3f5ff9a8889 100644 --- a/packages/edit-post/src/components/start-page-options/style.scss +++ b/packages/edit-post/src/components/start-page-options/style.scss @@ -3,9 +3,6 @@ column-count: 2; column-gap: $grid-unit-30; - // Small top padding required to avoid cutting off the visible outline when hovering items - padding-top: $border-width-focus-fallback; - @include break-medium() { column-count: 3; } diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/style.scss b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/style.scss index ae35cb4525b88b..012622f9b1040b 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/style.scss +++ b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/style.scss @@ -8,8 +8,3 @@ } } } - -.sidebar-navigation__rename-modal-form { - // Fix for input focus style being cut off by the modal. - padding-top: 1px; -} diff --git a/packages/edit-site/src/components/start-template-options/style.scss b/packages/edit-site/src/components/start-template-options/style.scss index 7515861099b380..53cd38a8c9bfad 100644 --- a/packages/edit-site/src/components/start-template-options/style.scss +++ b/packages/edit-site/src/components/start-template-options/style.scss @@ -29,9 +29,6 @@ $actions-height: 92px; column-count: 2; column-gap: $grid-unit-30; - // Small top padding required to avoid cutting off the visible outline when hovering items - padding-top: $border-width-focus-fallback; - @include break-medium() { column-count: 3; } From 2d1d0facf8051ff9a48e505fd099453d9cb7f29a Mon Sep 17 00:00:00 2001 From: Ramon Date: Tue, 27 Jun 2023 19:24:39 +1000 Subject: [PATCH 025/266] Rest API: rename navigation fallback classes from WP_ to Gutenberg_ (#51959) * The `WP_REST_Navigation_Fallback_Controller` class has been committed to core and therefore results in a naming conflict and unit test failures. Ideally `WP_REST_Navigation_Fallback_Controller` should have been named `WP_REST_Navigation_Fallback_Controller_Gutenberg` and extended `WP_REST_Navigation_Fallback_Controller`. But we can conditionally load the file instead. * Renamed WP_Classic_To_Block_Menu_Converter to Gutenberg_Classic_To_Block_Menu_Converter Load WP_REST_Navigation_Fallback_Controller dependencies in load.php * Renamed all 6.3 classes to have the Gutenberg_ prefix. This should avoid compat errors and hopefully some confusion later. * Also rename test files for completeness * Updated deprecation notices to refer to Gutenberg classes --- ...nberg-classic-to-block-menu-converter.php} | 4 +-- ...> class-gutenberg-navigation-fallback.php} | 11 ++----- ...g-rest-navigation-fallback-controller.php} | 11 ++----- lib/compat/wordpress-6.3/rest-api.php | 10 +++++++ lib/experimental/rest-api.php | 10 ------- lib/load.php | 4 ++- .../block-library/src/navigation/index.php | 12 ++++---- ...-classic-to-block-menu-converter-test.php} | 16 +++++----- ...rg-navigation-fallback-gutenberg-test.php} | 30 +++++++++---------- ...t-navigation-fallback-controller-test.php} | 4 +-- 10 files changed, 52 insertions(+), 60 deletions(-) rename lib/compat/wordpress-6.3/{class-wp-classic-to-block-menu-converter.php => class-gutenberg-classic-to-block-menu-converter.php} (97%) rename lib/compat/wordpress-6.3/{class-wp-navigation-fallback-gutenberg.php => class-gutenberg-navigation-fallback.php} (95%) rename lib/compat/wordpress-6.3/{class-wp-rest-navigation-fallback-controller.php => class-gutenberg-rest-navigation-fallback-controller.php} (94%) rename phpunit/{class-wp-classic-to-block-menu-converter-test.php => class-gutenberg-classic-to-block-menu-converter-test.php} (90%) rename phpunit/{class-wp-navigation-fallback-gutenberg-test.php => class-gutenberg-navigation-fallback-gutenberg-test.php} (91%) rename phpunit/{class-wp-rest-navigation-fallback-controller-test.php => class-gutenberg-rest-navigation-fallback-controller-test.php} (96%) diff --git a/lib/compat/wordpress-6.3/class-wp-classic-to-block-menu-converter.php b/lib/compat/wordpress-6.3/class-gutenberg-classic-to-block-menu-converter.php similarity index 97% rename from lib/compat/wordpress-6.3/class-wp-classic-to-block-menu-converter.php rename to lib/compat/wordpress-6.3/class-gutenberg-classic-to-block-menu-converter.php index 65c8a6d5bcba74..8677f9abaee170 100644 --- a/lib/compat/wordpress-6.3/class-wp-classic-to-block-menu-converter.php +++ b/lib/compat/wordpress-6.3/class-gutenberg-classic-to-block-menu-converter.php @@ -1,6 +1,6 @@ 404 ) ) ); diff --git a/lib/compat/wordpress-6.3/rest-api.php b/lib/compat/wordpress-6.3/rest-api.php index 87b52ed9b8992a..144ad4d50c83f1 100644 --- a/lib/compat/wordpress-6.3/rest-api.php +++ b/lib/compat/wordpress-6.3/rest-api.php @@ -109,3 +109,13 @@ function gutenberg_register_rest_block_patterns() { $block_patterns->register_routes(); } add_action( 'rest_api_init', 'gutenberg_register_rest_block_patterns' ); + + +/** + * Registers the Navigation Fallbacks REST API routes. + */ +function gutenberg_register_rest_navigation_fallbacks() { + $editor_settings = new Gutenberg_REST_Navigation_Fallback_Controller(); + $editor_settings->register_routes(); +} +add_action( 'rest_api_init', 'gutenberg_register_rest_navigation_fallbacks' ); diff --git a/lib/experimental/rest-api.php b/lib/experimental/rest-api.php index 4a8bbea1548a6f..2fc7a7af576f82 100644 --- a/lib/experimental/rest-api.php +++ b/lib/experimental/rest-api.php @@ -29,16 +29,6 @@ function gutenberg_register_block_editor_settings() { add_action( 'rest_api_init', 'gutenberg_register_block_editor_settings' ); -/** - * Registers the Navigation Fallbacks REST API routes. - */ -function gutenberg_register_rest_navigation_fallbacks() { - $editor_settings = new WP_REST_Navigation_Fallback_Controller(); - $editor_settings->register_routes(); -} -add_action( 'rest_api_init', 'gutenberg_register_rest_navigation_fallbacks' ); - - /** * Shim for get_sample_permalink() to add support for auto-draft status. * diff --git a/lib/load.php b/lib/load.php index 0ef72a48ebdbbe..a390263fa119da 100644 --- a/lib/load.php +++ b/lib/load.php @@ -48,7 +48,9 @@ function gutenberg_is_experiment_enabled( $name ) { require_once __DIR__ . '/compat/wordpress-6.3/class-gutenberg-rest-templates-controller-6-3.php'; require_once __DIR__ . '/compat/wordpress-6.3/class-gutenberg-rest-global-styles-controller-6-3.php'; require_once __DIR__ . '/compat/wordpress-6.3/class-gutenberg-rest-global-styles-revisions-controller-6-3.php'; - require_once __DIR__ . '/compat/wordpress-6.3/class-wp-rest-navigation-fallback-controller.php'; + require_once __DIR__ . '/compat/wordpress-6.3/class-gutenberg-classic-to-block-menu-converter.php'; + require_once __DIR__ . '/compat/wordpress-6.3/class-gutenberg-navigation-fallback.php'; + require_once __DIR__ . '/compat/wordpress-6.3/class-gutenberg-rest-navigation-fallback-controller.php'; require_once __DIR__ . '/compat/wordpress-6.3/rest-api.php'; require_once __DIR__ . '/compat/wordpress-6.3/theme-previews.php'; require_once __DIR__ . '/compat/wordpress-6.3/navigation-block-preloading.php'; diff --git a/packages/block-library/src/navigation/index.php b/packages/block-library/src/navigation/index.php index f8f53a93e70fd9..d16de821b47125 100644 --- a/packages/block-library/src/navigation/index.php +++ b/packages/block-library/src/navigation/index.php @@ -341,7 +341,7 @@ function block_core_navigation_get_fallback_blocks() { // If `core/page-list` is not registered then return empty blocks. $fallback_blocks = $registry->is_registered( 'core/page-list' ) ? $page_list_fallback : array(); - $navigation_post = WP_Navigation_Fallback_Gutenberg::get_fallback(); + $navigation_post = Gutenberg_Navigation_Fallback::get_fallback(); // Use the first non-empty Navigation as fallback if available. if ( $navigation_post ) { @@ -846,7 +846,7 @@ function block_core_navigation_typographic_presets_backcompatibility( $parsed_bl */ function block_core_navigation_parse_blocks_from_menu_items( $menu_items, $menu_items_by_parent_id ) { - _deprecated_function( __FUNCTION__, '6.3.0', 'WP_Navigation_Fallback_Gutenberg::parse_blocks_from_menu_items' ); + _deprecated_function( __FUNCTION__, '6.3.0', 'Gutenberg_Navigation_Fallback::parse_blocks_from_menu_items' ); if ( empty( $menu_items ) ) { return array(); @@ -895,7 +895,7 @@ function block_core_navigation_parse_blocks_from_menu_items( $menu_items, $menu_ */ function block_core_navigation_get_classic_menu_fallback() { - _deprecated_function( __FUNCTION__, '6.3.0', 'WP_Navigation_Fallback_Gutenberg::get_classic_menu_fallback' ); + _deprecated_function( __FUNCTION__, '6.3.0', 'Gutenberg_Navigation_Fallback::get_classic_menu_fallback' ); $classic_nav_menus = wp_get_nav_menus(); @@ -938,7 +938,7 @@ static function( $a, $b ) { */ function block_core_navigation_get_classic_menu_fallback_blocks( $classic_nav_menu ) { - _deprecated_function( __FUNCTION__, '6.3.0', 'WP_Navigation_Fallback_Gutenberg::get_classic_menu_fallback_blocks' ); + _deprecated_function( __FUNCTION__, '6.3.0', 'Gutenberg_Navigation_Fallback::get_classic_menu_fallback_blocks' ); // BEGIN: Code that already exists in wp_nav_menu(). $menu_items = wp_get_nav_menu_items( $classic_nav_menu->term_id, array( 'update_post_term_cache' => false ) ); @@ -977,7 +977,7 @@ function block_core_navigation_get_classic_menu_fallback_blocks( $classic_nav_me */ function block_core_navigation_maybe_use_classic_menu_fallback() { - _deprecated_function( __FUNCTION__, '6.3.0', 'WP_Navigation_Fallback_Gutenberg::create_classic_menu_fallback' ); + _deprecated_function( __FUNCTION__, '6.3.0', 'Gutenberg_Navigation_Fallback::create_classic_menu_fallback' ); // See if we have a classic menu. $classic_nav_menu = block_core_navigation_get_classic_menu_fallback(); @@ -1020,7 +1020,7 @@ function block_core_navigation_maybe_use_classic_menu_fallback() { */ function block_core_navigation_get_most_recently_published_navigation() { - _deprecated_function( __FUNCTION__, '6.3.0', 'WP_Navigation_Fallback_Gutenberg::get_most_recently_published_navigation' ); + _deprecated_function( __FUNCTION__, '6.3.0', 'Gutenberg_Navigation_Fallback::get_most_recently_published_navigation' ); // Default to the most recently created menu. $parsed_args = array( diff --git a/phpunit/class-wp-classic-to-block-menu-converter-test.php b/phpunit/class-gutenberg-classic-to-block-menu-converter-test.php similarity index 90% rename from phpunit/class-wp-classic-to-block-menu-converter-test.php rename to phpunit/class-gutenberg-classic-to-block-menu-converter-test.php index 0d7670d8ec867e..5f0a8ceee52a10 100644 --- a/phpunit/class-wp-classic-to-block-menu-converter-test.php +++ b/phpunit/class-gutenberg-classic-to-block-menu-converter-test.php @@ -1,20 +1,20 @@ assertTrue( class_exists( 'WP_Classic_To_Block_Menu_Converter' ) ); + $this->assertTrue( class_exists( 'Gutenberg_Classic_To_Block_Menu_Converter' ) ); } /** @@ -23,7 +23,7 @@ public function test_class_exists() { */ public function test_passing_non_menu_object_to_converter_returns_wp_error( $data ) { - $result = WP_Classic_To_Block_Menu_Converter::convert( $data ); + $result = Gutenberg_Classic_To_Block_Menu_Converter::convert( $data ); $this->assertTrue( is_wp_error( $result ), 'Should be a WP_Error instance' ); @@ -88,7 +88,7 @@ public function test_can_convert_classic_menu_to_blocks() { $classic_nav_menu = wp_get_nav_menu_object( $menu_id ); - $blocks = WP_Classic_To_Block_Menu_Converter::convert( $classic_nav_menu ); + $blocks = Gutenberg_Classic_To_Block_Menu_Converter::convert( $classic_nav_menu ); $this->assertNotEmpty( $blocks ); @@ -179,7 +179,7 @@ public function test_does_not_convert_menu_items_with_non_publish_status() { $classic_nav_menu = wp_get_nav_menu_object( $menu_id ); - $blocks = WP_Classic_To_Block_Menu_Converter::convert( $classic_nav_menu ); + $blocks = Gutenberg_Classic_To_Block_Menu_Converter::convert( $classic_nav_menu ); $this->assertNotEmpty( $blocks ); @@ -204,7 +204,7 @@ public function test_returns_empty_array_for_menus_with_no_items() { $classic_nav_menu = wp_get_nav_menu_object( $menu_id ); - $blocks = WP_Classic_To_Block_Menu_Converter::convert( $classic_nav_menu ); + $blocks = Gutenberg_Classic_To_Block_Menu_Converter::convert( $classic_nav_menu ); $this->assertEmpty( $blocks, 'Result should be empty.' ); diff --git a/phpunit/class-wp-navigation-fallback-gutenberg-test.php b/phpunit/class-gutenberg-navigation-fallback-gutenberg-test.php similarity index 91% rename from phpunit/class-wp-navigation-fallback-gutenberg-test.php rename to phpunit/class-gutenberg-navigation-fallback-gutenberg-test.php index fe32e8a640c078..7b0719950df8b5 100644 --- a/phpunit/class-wp-navigation-fallback-gutenberg-test.php +++ b/phpunit/class-gutenberg-navigation-fallback-gutenberg-test.php @@ -1,14 +1,14 @@ assertTrue( class_exists( 'WP_Navigation_Fallback_Gutenberg' ), 'WP_Navigation_Fallback_Gutenberg class should exist.' ); + $this->assertTrue( class_exists( 'Gutenberg_Navigation_Fallback' ), 'Gutenberg_Navigation_Fallback class should exist.' ); } @@ -37,7 +37,7 @@ public function test_it_exists() { * @covers WP_REST_Navigation_Fallback_Controller::get_fallback */ public function test_should_return_a_default_fallback_navigation_menu_in_absence_of_other_fallbacks() { - $data = WP_Navigation_Fallback_Gutenberg::get_fallback(); + $data = Gutenberg_Navigation_Fallback::get_fallback(); $this->assertInstanceOf( 'WP_Post', $data, 'Response should be of the correct type.' ); @@ -63,7 +63,7 @@ public function test_should_return_a_default_fallback_navigation_menu_with_no_bl unregister_block_type( 'core/page-list' ); - $data = WP_Navigation_Fallback_Gutenberg::get_fallback(); + $data = Gutenberg_Navigation_Fallback::get_fallback(); $this->assertInstanceOf( 'WP_Post', $data, 'Response should be of the correct type.' ); @@ -79,11 +79,11 @@ public function test_should_return_a_default_fallback_navigation_menu_with_no_bl */ public function test_should_handle_consecutive_invocations() { // Invoke the method multiple times to ensure that it doesn't create a new fallback menu on each invocation. - WP_Navigation_Fallback_Gutenberg::get_fallback(); - WP_Navigation_Fallback_Gutenberg::get_fallback(); + Gutenberg_Navigation_Fallback::get_fallback(); + Gutenberg_Navigation_Fallback::get_fallback(); // Assert on the final invocation. - $data = WP_Navigation_Fallback_Gutenberg::get_fallback(); + $data = Gutenberg_Navigation_Fallback::get_fallback(); $this->assertInstanceOf( 'WP_Post', $data, 'Response should be of the correct type.' ); @@ -115,7 +115,7 @@ public function test_should_return_the_most_recently_created_navigation_menu() { ) ); - $data = WP_Navigation_Fallback_Gutenberg::get_fallback(); + $data = Gutenberg_Navigation_Fallback::get_fallback(); $this->assertInstanceOf( 'WP_Post', $data, 'Response should be of the correct type.' ); @@ -147,7 +147,7 @@ public function test_should_return_fallback_navigation_from_existing_classic_men ) ); - $data = WP_Navigation_Fallback_Gutenberg::get_fallback(); + $data = Gutenberg_Navigation_Fallback::get_fallback(); $this->assertInstanceOf( 'WP_Post', $data, 'Response should be of the correct type.' ); @@ -201,7 +201,7 @@ public function test_should_prioritise_fallback_to_classic_menu_in_primary_locat $locations['header'] = $another_menu_id; set_theme_mod( 'nav_menu_locations', $locations ); - $data = WP_Navigation_Fallback_Gutenberg::get_fallback(); + $data = Gutenberg_Navigation_Fallback::get_fallback(); $this->assertInstanceOf( 'WP_Post', $data, 'Response should be of the correct type.' ); @@ -238,7 +238,7 @@ public function test_should_fallback_to_classic_menu_with_primary_slug() { ) ); - $data = WP_Navigation_Fallback_Gutenberg::get_fallback(); + $data = Gutenberg_Navigation_Fallback::get_fallback(); $this->assertInstanceOf( 'WP_Post', $data, 'Response should be of the correct type.' ); @@ -275,7 +275,7 @@ public function test_should_fallback_to_most_recently_created_classic_menu() { ) ); - $data = WP_Navigation_Fallback_Gutenberg::get_fallback(); + $data = Gutenberg_Navigation_Fallback::get_fallback(); $this->assertInstanceOf( 'WP_Post', $data, 'Response should be of the correct type.' ); @@ -306,7 +306,7 @@ public function test_should_not_create_fallback_from_classic_menu_if_a_navigatio ) ); - $data = WP_Navigation_Fallback_Gutenberg::get_fallback(); + $data = Gutenberg_Navigation_Fallback::get_fallback(); $this->assertInstanceOf( 'WP_Post', $data, 'Response should be of the correct type.' ); diff --git a/phpunit/class-wp-rest-navigation-fallback-controller-test.php b/phpunit/class-gutenberg-rest-navigation-fallback-controller-test.php similarity index 96% rename from phpunit/class-wp-rest-navigation-fallback-controller-test.php rename to phpunit/class-gutenberg-rest-navigation-fallback-controller-test.php index 1cdedb568a5cd7..14ab8ee0971dd9 100644 --- a/phpunit/class-wp-rest-navigation-fallback-controller-test.php +++ b/phpunit/class-gutenberg-rest-navigation-fallback-controller-test.php @@ -1,6 +1,6 @@ Date: Tue, 27 Jun 2023 11:39:41 +0100 Subject: [PATCH 026/266] Update text color (#51965) --- .../edit-site/src/components/sidebar-navigation-item/style.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/edit-site/src/components/sidebar-navigation-item/style.scss b/packages/edit-site/src/components/sidebar-navigation-item/style.scss index d032bece171075..d81b6764f7b721 100644 --- a/packages/edit-site/src/components/sidebar-navigation-item/style.scss +++ b/packages/edit-site/src/components/sidebar-navigation-item/style.scss @@ -15,6 +15,7 @@ &[aria-current] { background: var(--wp-admin-theme-color); + color: $white; } .edit-site-sidebar-navigation-item__drilldown-indicator { From 69d07900f5322939282f795ad8662ed31c3307f3 Mon Sep 17 00:00:00 2001 From: Dave Smith Date: Tue, 27 Jun 2023 11:59:46 +0100 Subject: [PATCH 027/266] Restore sidebar in focus mode on Pattern click through in Browse Mode Library (#51897) Brings back https://github.com/WordPress/gutenberg/pull/51492 --- packages/edit-site/src/components/page-library/grid-item.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/edit-site/src/components/page-library/grid-item.js b/packages/edit-site/src/components/page-library/grid-item.js index 027e14ca9e9467..586602d1ac05d3 100644 --- a/packages/edit-site/src/components/page-library/grid-item.js +++ b/packages/edit-site/src/components/page-library/grid-item.js @@ -46,7 +46,7 @@ export default function GridItem( { categoryId, composite, icon, item } ) { postId: item.type === USER_PATTERNS ? item.id : item.name, categoryId, categoryType: item.type, - canvas: 'edit', + canvas: 'view', } ); const onKeyDown = ( event ) => { From d9c53e194fcc58c8f60817e4e3200b091f5f9d7b Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Fri, 16 Jun 2023 12:18:18 +0300 Subject: [PATCH 028/266] Perf: Rely on a single store listener only Currently when a component calls useSelect or any of a number of other hooks, the store creates a new listener through Redux's subscribe method. In this patch we're storing a custom list of listeners before calling into Redux and relying on a single Redux listener to call all of the registered functions. This reduces the number of comparisons to `getState()` when updtaing the store and measured a reduction in several key metrics by between 4% to 9%; specifically the `type`, `focus`, and `inserter` methods. Co-authored-by: Jarda Snajdr --- packages/data/src/redux-store/index.js | 40 +++++++++++++++++++------- packages/data/src/types.ts | 6 ++++ 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/packages/data/src/redux-store/index.js b/packages/data/src/redux-store/index.js index 7b1c0d2991e69d..933cd11f3ddba1 100644 --- a/packages/data/src/redux-store/index.js +++ b/packages/data/src/redux-store/index.js @@ -24,6 +24,7 @@ import * as metadataSelectors from './metadata/selectors'; import * as metadataActions from './metadata/actions'; /** @typedef {import('../types').DataRegistry} DataRegistry */ +/** @typedef {import('../types').ListenerFunction} ListenerFunction */ /** * @typedef {import('../types').StoreDescriptor} StoreDescriptor * @template {import('../types').AnyConfig} C @@ -158,6 +159,19 @@ export default function createReduxStore( key, options ) { const storeDescriptor = { name: key, instantiate: ( registry ) => { + /** + * Stores listener functions registered with `subscribe()`. + * + * When functions register to listen to store changes with + * `subscribe()` they get added here. Although Redux offers + * its own `subscribe()` function directly, by wrapping the + * subscription in this store instance it's possible to + * optimize checking if the state has changed before calling + * each listener. + * + * @type {Set} + */ + const listeners = new Set(); const reducer = options.reducer; const thunkArgs = { registry, @@ -290,18 +304,24 @@ export default function createReduxStore( key, options ) { const subscribe = store && ( ( listener ) => { - let lastState = store.__unstableOriginalGetState(); - return store.subscribe( () => { - const state = store.__unstableOriginalGetState(); - const hasChanged = state !== lastState; - lastState = state; - - if ( hasChanged ) { - listener(); - } - } ); + listeners.add( listener ); + + return () => listeners.delete( listener ); } ); + let lastState = store.__unstableOriginalGetState(); + store.subscribe( () => { + const state = store.__unstableOriginalGetState(); + const hasChanged = state !== lastState; + lastState = state; + + if ( hasChanged ) { + for ( const listener of listeners ) { + listener(); + } + } + } ); + // This can be simplified to just { subscribe, getSelectors, getActions } // Once we remove the use function. return { diff --git a/packages/data/src/types.ts b/packages/data/src/types.ts index fe7d9061d3c5d8..defb7218457c7c 100644 --- a/packages/data/src/types.ts +++ b/packages/data/src/types.ts @@ -70,6 +70,12 @@ export type MapSelect = ( export type SelectFunction = < S >( store: S ) => CurriedSelectorsOf< S >; +/** + * Callback for store's `subscribe()` method that + * runs when the store data has changed. + */ +export type ListenerFunction = () => void; + export type CurriedSelectorsOf< S > = S extends StoreDescriptor< ReduxStoreConfig< any, any, infer Selectors > > From 33fc3959119b51e96ec5088158a5a5e4e4760d73 Mon Sep 17 00:00:00 2001 From: Marin Atanasov <8436925+tyxla@users.noreply.github.com> Date: Tue, 27 Jun 2023 15:33:11 +0300 Subject: [PATCH 029/266] Lodash: Refactor embed block away from `_.kebabCase()` (#51916) * Lodash: Refactor embed block away from _.kebabCase() * Expose kebabCase for RN --- packages/block-editor/src/private-apis.js | 2 ++ packages/block-editor/src/private-apis.native.js | 2 ++ packages/block-library/src/embed/util.js | 10 ++++------ 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/block-editor/src/private-apis.js b/packages/block-editor/src/private-apis.js index dd8d2d8ff411f7..1200dee367d243 100644 --- a/packages/block-editor/src/private-apis.js +++ b/packages/block-editor/src/private-apis.js @@ -5,6 +5,7 @@ import * as globalStyles from './components/global-styles'; import { ExperimentalBlockEditorProvider } from './components/provider'; import { lock } from './lock-unlock'; import { getRichTextValues } from './components/rich-text/content'; +import { kebabCase } from './utils/object'; import ResizableBoxPopover from './components/resizable-box-popover'; import { ComposedPrivateInserter as PrivateInserter } from './components/inserter'; import { PrivateListView } from './components/list-view'; @@ -27,6 +28,7 @@ lock( privateApis, { ...globalStyles, ExperimentalBlockEditorProvider, getRichTextValues, + kebabCase, PrivateInserter, PrivateListView, ResizableBoxPopover, diff --git a/packages/block-editor/src/private-apis.native.js b/packages/block-editor/src/private-apis.native.js index 5555e00477e7b5..17676f634b1cae 100644 --- a/packages/block-editor/src/private-apis.native.js +++ b/packages/block-editor/src/private-apis.native.js @@ -3,6 +3,7 @@ */ import * as globalStyles from './components/global-styles'; import { ExperimentalBlockEditorProvider } from './components/provider'; +import { kebabCase } from './utils/object'; import { lock } from './lock-unlock'; /** @@ -11,5 +12,6 @@ import { lock } from './lock-unlock'; export const privateApis = {}; lock( privateApis, { ...globalStyles, + kebabCase, ExperimentalBlockEditorProvider, } ); diff --git a/packages/block-library/src/embed/util.js b/packages/block-library/src/embed/util.js index 609a46293666e8..a7a6ea219f2772 100644 --- a/packages/block-library/src/embed/util.js +++ b/packages/block-library/src/embed/util.js @@ -1,18 +1,13 @@ -/** - * Internal dependencies - */ -import { ASPECT_RATIOS, WP_EMBED_TYPE } from './constants'; - /** * External dependencies */ -import { kebabCase } from 'lodash'; import classnames from 'classnames/dedupe'; import memoize from 'memize'; /** * WordPress dependencies */ +import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; import { renderToString } from '@wordpress/element'; import { createBlock, @@ -24,8 +19,11 @@ import { * Internal dependencies */ import metadata from './block.json'; +import { ASPECT_RATIOS, WP_EMBED_TYPE } from './constants'; +import { unlock } from '../lock-unlock'; const { name: DEFAULT_EMBED_BLOCK } = metadata; +const { kebabCase } = unlock( blockEditorPrivateApis ); /** @typedef {import('@wordpress/blocks').WPBlockVariation} WPBlockVariation */ From 31d691d3f782945f7d1cacdff276a2a75881ddaf Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 27 Jun 2023 14:12:17 +0100 Subject: [PATCH 030/266] Updating the BlockEditorProvider settings prop should reset the store's settings entirely (#51904) Co-authored-by: Dave Smith --- .../src/components/provider/index.js | 11 +++- packages/block-editor/src/store/actions.js | 4 +- .../block-editor/src/store/private-actions.js | 9 ++- packages/block-editor/src/store/reducer.js | 6 ++ .../src/store/test/private-actions.js | 56 +++++++++++++++++++ .../block-editor/use-site-editor-settings.js | 8 --- 6 files changed, 80 insertions(+), 14 deletions(-) diff --git a/packages/block-editor/src/components/provider/index.js b/packages/block-editor/src/components/provider/index.js index dbd646426718de..13d25aafd8c832 100644 --- a/packages/block-editor/src/components/provider/index.js +++ b/packages/block-editor/src/components/provider/index.js @@ -28,9 +28,16 @@ export const ExperimentalBlockEditorProvider = withRegistryProvider( ...settings, __internalIsInitialized: true, }, - stripExperimentalSettings + { + stripExperimentalSettings, + reset: true, + } ); - }, [ settings ] ); + }, [ + settings, + stripExperimentalSettings, + __experimentalUpdateSettings, + ] ); // Syncs the entity provider with changes in the block-editor store. useBlockSync( props ); diff --git a/packages/block-editor/src/store/actions.js b/packages/block-editor/src/store/actions.js index 09ff7e185ab54d..9dd3b24009c512 100644 --- a/packages/block-editor/src/store/actions.js +++ b/packages/block-editor/src/store/actions.js @@ -1390,7 +1390,9 @@ export function updateBlockListSettings( clientId, settings ) { * @return {Object} Action object */ export function updateSettings( settings ) { - return __experimentalUpdateSettings( settings, true ); + return __experimentalUpdateSettings( settings, { + stripExperimentalSettings: true, + } ); } /** diff --git a/packages/block-editor/src/store/private-actions.js b/packages/block-editor/src/store/private-actions.js index 74c83d60189692..8b5e066e5a83c0 100644 --- a/packages/block-editor/src/store/private-actions.js +++ b/packages/block-editor/src/store/private-actions.js @@ -28,13 +28,15 @@ const privateSettings = [ * Action that updates the block editor settings and * conditionally preserves the experimental ones. * - * @param {Object} settings Updated settings - * @param {boolean} stripExperimentalSettings Whether to strip experimental settings. + * @param {Object} settings Updated settings + * @param {Object} options Options object. + * @param {boolean} options.stripExperimentalSettings Whether to strip experimental settings. + * @param {boolean} options.reset Whether to reset the settings. * @return {Object} Action object */ export function __experimentalUpdateSettings( settings, - stripExperimentalSettings = false + { stripExperimentalSettings = false, reset = false } = {} ) { let cleanSettings = settings; // There are no plugins in the mobile apps, so there is no @@ -50,6 +52,7 @@ export function __experimentalUpdateSettings( return { type: 'UPDATE_SETTINGS', settings: cleanSettings, + reset, }; } diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js index d23f5f30589788..b4104a69ac620b 100644 --- a/packages/block-editor/src/store/reducer.js +++ b/packages/block-editor/src/store/reducer.js @@ -1623,6 +1623,12 @@ export function template( state = { isValid: true }, action ) { export function settings( state = SETTINGS_DEFAULTS, action ) { switch ( action.type ) { case 'UPDATE_SETTINGS': + if ( action.reset ) { + return { + ...SETTINGS_DEFAULTS, + ...action.settings, + }; + } return { ...state, ...action.settings, diff --git a/packages/block-editor/src/store/test/private-actions.js b/packages/block-editor/src/store/test/private-actions.js index fdfe993091fef7..01f6b85f5f9aa6 100644 --- a/packages/block-editor/src/store/test/private-actions.js +++ b/packages/block-editor/src/store/test/private-actions.js @@ -6,6 +6,7 @@ import { showBlockInterface, setBlockEditingMode, unsetBlockEditingMode, + __experimentalUpdateSettings, } from '../private-actions'; describe( 'private actions', () => { @@ -50,4 +51,59 @@ describe( 'private actions', () => { } ); } ); } ); + + describe( '__experimentalUpdateSettings', () => { + const experimentalSettings = { + inserterMediaCategories: 'foo', + blockInspectorAnimation: 'bar', + }; + + const stableSettings = { + foo: 'foo', + bar: 'bar', + baz: 'baz', + }; + + const settings = { + ...experimentalSettings, + ...stableSettings, + }; + + it( 'should dispatch provided settings by default', () => { + expect( __experimentalUpdateSettings( settings ) ).toEqual( { + type: 'UPDATE_SETTINGS', + settings, + reset: false, + } ); + } ); + + it( 'should dispatch provided settings with reset flag when `reset` argument is truthy', () => { + expect( + __experimentalUpdateSettings( settings, { + stripExperimentalSettings: false, + reset: true, + } ) + ).toEqual( { + type: 'UPDATE_SETTINGS', + settings, + reset: true, + } ); + } ); + + it( 'should strip experimental settings from a given settings object when `stripExperimentalSettings` argument is truthy', () => { + expect( + __experimentalUpdateSettings( settings, { + stripExperimentalSettings: true, + } ) + ).toEqual( { + type: 'UPDATE_SETTINGS', + settings: { + foo: 'foo', + bar: 'bar', + baz: 'baz', + }, + reset: false, + } ); + } ); + } ); } ); diff --git a/packages/edit-site/src/components/block-editor/use-site-editor-settings.js b/packages/edit-site/src/components/block-editor/use-site-editor-settings.js index af3f5ccba3498f..4ce186ad9d6bb5 100644 --- a/packages/edit-site/src/components/block-editor/use-site-editor-settings.js +++ b/packages/edit-site/src/components/block-editor/use-site-editor-settings.js @@ -78,14 +78,6 @@ export default function useSiteEditorSettings( templateType ) { inserterMediaCategories, __experimentalBlockPatterns: blockPatterns, __experimentalBlockPatternCategories: blockPatternCategories, - // Temporary fix for bug in Block Editor Provider: - // see: https://github.com/WordPress/gutenberg/issues/51489. - // Some Site Editor entities (e.g. `wp_navigation`) may utilise - // template locking in their settings. Therefore this must be - // explicitly "unset" to avoid the template locking UI remaining - // active for all entities. - templateLock: false, - template: false, }; }, [ storedSettings, blockPatterns, blockPatternCategories ] ); } From 40f841cb9b2e039df317619ccd1307ddf41b3a83 Mon Sep 17 00:00:00 2001 From: Rich Tabor Date: Tue, 27 Jun 2023 09:31:58 -0400 Subject: [PATCH 031/266] RangeControl: Add support for large 40px number input size (#49105) Co-authored-by: chad1008 <13856531+chad1008@users.noreply.github.com> --- .../src/components/block-inspector/style.scss | 3 ++- .../src/cover/edit/inspector-controls.js | 1 + packages/components/CHANGELOG.md | 1 + packages/components/src/number-control/index.tsx | 1 - packages/components/src/range-control/index.tsx | 16 ++++++++++++++-- .../components/src/range-control/input-range.tsx | 1 - .../range-control/styles/range-control-styles.ts | 15 ++++++++++++--- packages/components/src/range-control/types.ts | 6 ++++++ 8 files changed, 36 insertions(+), 8 deletions(-) diff --git a/packages/block-editor/src/components/block-inspector/style.scss b/packages/block-editor/src/components/block-inspector/style.scss index 5cf4a8b382f998..e57b54bd32c826 100644 --- a/packages/block-editor/src/components/block-inspector/style.scss +++ b/packages/block-editor/src/components/block-inspector/style.scss @@ -20,7 +20,8 @@ // Reset unwanted margin-bottom from being applied to BaseControls within certain components. .components-focal-point-picker-control, - .components-query-controls { + .components-query-controls, + .components-range-control { .components-base-control { margin-bottom: 0; } diff --git a/packages/block-library/src/cover/edit/inspector-controls.js b/packages/block-library/src/cover/edit/inspector-controls.js index d5a692350993fd..e8f3683e4d95ff 100644 --- a/packages/block-library/src/cover/edit/inspector-controls.js +++ b/packages/block-library/src/cover/edit/inspector-controls.js @@ -303,6 +303,7 @@ export default function CoverInspectorControls( { max={ 100 } step={ 10 } required + __next40pxDefaultSize /> diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 6409ce7515646f..19182873e38282 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -6,6 +6,7 @@ - `SelectControl`: Added option to set hidden options. ([#51545](https://github.com/WordPress/gutenberg/pull/51545)) - `UnitControl`: Revamp support for changing unit by typing ([#39303](https://github.com/WordPress/gutenberg/pull/39303)). +- `RangeControl`: Add `__next40pxDefaultSize` prop to opt into the new 40px default size ([#49105](https://github.com/WordPress/gutenberg/pull/49105)). - `Modal`: Update corner radius to be between buttons and the site view frame, in a 2-4-8 system. ([#51254](https://github.com/WordPress/gutenberg/pull/51254)). - `Button`: Introduce `size` prop with `default`, `compact`, and `small` variants ([#51842](https://github.com/WordPress/gutenberg/pull/51842)). - `ItemGroup`: Update button focus state styles to be inline with other button focus states in the editor. ([#51576](https://github.com/WordPress/gutenberg/pull/51576)). diff --git a/packages/components/src/number-control/index.tsx b/packages/components/src/number-control/index.tsx index b2ab417d18af21..d2ce9d8ed7d019 100644 --- a/packages/components/src/number-control/index.tsx +++ b/packages/components/src/number-control/index.tsx @@ -60,7 +60,6 @@ function UnforwardedNumberControl( } ); spinControls = 'none'; } - const inputRef = useRef< HTMLInputElement >(); const mergedRef = useMergeRefs( [ inputRef, forwardedRef ] ); diff --git a/packages/components/src/range-control/index.tsx b/packages/components/src/range-control/index.tsx index 928595132e2540..19630e39f6bfe9 100644 --- a/packages/components/src/range-control/index.tsx +++ b/packages/components/src/range-control/index.tsx @@ -37,6 +37,7 @@ import { import type { RangeControlProps } from './types'; import type { WordPressComponentProps } from '../ui/context'; +import { space } from '../ui/utils/space'; const noop = () => {}; @@ -69,6 +70,7 @@ function UnforwardedRangeControl( railColor, renderTooltipContent = ( v ) => v, resetFallbackValue, + __next40pxDefaultSize = false, shiftStep = 10, showTooltip: showTooltipProp, step = 1, @@ -208,7 +210,6 @@ function UnforwardedRangeControl( const offsetStyle = { [ isRTL() ? 'right' : 'left' ]: fillValueOffset, }; - return ( - + { beforeIcon && ( @@ -305,6 +309,14 @@ function UnforwardedRangeControl( onBlur={ handleOnInputNumberBlur } onChange={ handleOnChange } shiftStep={ shiftStep } + size={ + __next40pxDefaultSize + ? '__unstable-large' + : 'default' + } + __unstableInputWidth={ + __next40pxDefaultSize ? space( 20 ) : space( 16 ) + } step={ step } // @ts-expect-error TODO: Investigate if the `null` value is necessary value={ inputSliderValue } diff --git a/packages/components/src/range-control/input-range.tsx b/packages/components/src/range-control/input-range.tsx index 3b663309918ae1..1daa351444686a 100644 --- a/packages/components/src/range-control/input-range.tsx +++ b/packages/components/src/range-control/input-range.tsx @@ -16,7 +16,6 @@ function InputRange( ref: React.ForwardedRef< HTMLInputElement > ) { const { describedBy, label, value, ...otherProps } = props; - return ( css( { height: rangeHeightValue, minHeight: rangeHeightValue } ); const thumbSize = 12; -export const Root = styled.div` +const deprecatedHeight = ( { + __next40pxDefaultSize, +}: Pick< RangeControlProps, '__next40pxDefaultSize' > ) => + ! __next40pxDefaultSize && css( { minHeight: rangeHeightValue } ); + +type RootProps = Pick< RangeControlProps, '__next40pxDefaultSize' >; +export const Root = styled.div< RootProps >` -webkit-tap-highlight-color: transparent; - align-items: flex-start; + align-items: center; display: flex; justify-content: flex-start; padding: 0; position: relative; touch-action: none; width: 100%; + min-height: 40px; + /* TODO: remove after removing the __next40pxDefaultSize prop */ + ${ deprecatedHeight }; `; const wrapperColor = ( { color = COLORS.ui.borderFocus }: WrapperProps ) => @@ -296,7 +306,6 @@ export const InputNumber = styled( NumberControl )` display: inline-block; font-size: 13px; margin-top: 0; - width: ${ space( 16 ) } !important; input[type='number']& { ${ rangeHeight }; diff --git a/packages/components/src/range-control/types.ts b/packages/components/src/range-control/types.ts index 6d7d95a2e2bf41..4f7c5571fde9cf 100644 --- a/packages/components/src/range-control/types.ts +++ b/packages/components/src/range-control/types.ts @@ -203,6 +203,12 @@ export type RangeControlProps = Pick< * @default 10 */ shiftStep?: number; + /** + * Start opting into the larger default height that will become the default size in a future version. + * + * @default false + */ + __next40pxDefaultSize?: boolean; /** * Forcing the Tooltip UI to show or hide. This is overridden to `false` * when `step` is set to the special string value `any`. From f332703c4b1e5509e0c6f79aaed8c5b893b1161d Mon Sep 17 00:00:00 2001 From: mimi Date: Tue, 27 Jun 2023 23:11:50 +0900 Subject: [PATCH 032/266] Remove fill="none" from pinSmall icon (#51979) --- packages/icons/src/library/pin-small.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/icons/src/library/pin-small.js b/packages/icons/src/library/pin-small.js index 7ed84e47949a23..d479c27d77687a 100644 --- a/packages/icons/src/library/pin-small.js +++ b/packages/icons/src/library/pin-small.js @@ -8,7 +8,6 @@ const pinSmall = ( width="24" height="24" viewBox="0 0 24 24" - fill="none" xmlns="http://www.w3.org/2000/svg" > From c14126b803a3302d80f5b5817569019d97603e41 Mon Sep 17 00:00:00 2001 From: James Koster Date: Tue, 27 Jun 2023 15:17:52 +0100 Subject: [PATCH 033/266] Update stepper styling in Home template details panel (#51972) * Update stepper styling * Remove !important --- .../src/components/sidebar-navigation-screen/style.scss | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/edit-site/src/components/sidebar-navigation-screen/style.scss b/packages/edit-site/src/components/sidebar-navigation-screen/style.scss index dadee024b0eadd..7fe1cfc5925d63 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen/style.scss +++ b/packages/edit-site/src/components/sidebar-navigation-screen/style.scss @@ -107,7 +107,11 @@ .edit-site-sidebar-navigation-screen__input-control { width: 100%; .components-input-control__container { - background: transparent; + background: $gray-800; + + .components-button { + color: $gray-200; + } } .components-input-control__input { color: $gray-200 !important; From 6a02237fc954c9cc29a1227e0e84561a6731ea0a Mon Sep 17 00:00:00 2001 From: Nik Tsekouras Date: Tue, 27 Jun 2023 17:19:15 +0300 Subject: [PATCH 034/266] [Command center]: Add preferences and keyboard shortcuts commands (#51862) * [Command center]: Add preferences and keyboard shortcuts commands * update labels --- .../data/data-core-edit-post.md | 6 +++ .../header/preferences-menu-item/index.js | 7 +-- .../keyboard-shortcut-help-modal/index.js | 15 ++++-- .../src/components/preferences-modal/index.js | 10 ++-- .../src/hooks/commands/use-common-commands.js | 22 ++++++++ .../index.js | 7 +-- packages/edit-post/src/store/actions.js | 31 +++++++---- packages/edit-post/src/store/reducer.js | 20 ------- packages/edit-post/src/store/selectors.js | 14 +++-- packages/edit-post/src/store/test/reducer.js | 25 --------- .../edit-post/src/store/test/selectors.js | 27 ---------- .../header-edit-mode/more-menu/index.js | 53 +++++++++---------- .../keyboard-shortcut-help-modal/index.js | 28 +++++++--- .../src/components/preferences-modal/index.js | 20 ++++--- .../hooks/commands/use-edit-mode-commands.js | 26 ++++++++- packages/interface/src/store/actions.js | 25 +++++++++ packages/interface/src/store/reducer.js | 20 +++++++ packages/interface/src/store/selectors.js | 12 +++++ packages/interface/src/store/test/reducer.js | 30 +++++++++++ .../interface/src/store/test/selectors.js | 32 +++++++++++ 20 files changed, 288 insertions(+), 142 deletions(-) create mode 100644 packages/interface/src/store/test/reducer.js create mode 100644 packages/interface/src/store/test/selectors.js diff --git a/docs/reference-guides/data/data-core-edit-post.md b/docs/reference-guides/data/data-core-edit-post.md index aee88ce40bc731..b579e7e658007a 100644 --- a/docs/reference-guides/data/data-core-edit-post.md +++ b/docs/reference-guides/data/data-core-edit-post.md @@ -264,6 +264,8 @@ _Returns_ ### isModalActive +> **Deprecated** since WP 6.3 use `core/interface` store's selector with the same name instead. + Returns true if a modal is active, or false otherwise. _Parameters_ @@ -336,6 +338,8 @@ Returns an action object signalling that the user closed the sidebar. ### closeModal +> **Deprecated** since WP 6.3 use `core/interface` store's action with the same name instead. + Returns an action object signalling that the user closed a modal. _Returns_ @@ -388,6 +392,8 @@ _Parameters_ ### openModal +> **Deprecated** since WP 6.3 use `core/interface` store's action with the same name instead. + Returns an action object used in signalling that the user opened a modal. _Parameters_ diff --git a/packages/edit-post/src/components/header/preferences-menu-item/index.js b/packages/edit-post/src/components/header/preferences-menu-item/index.js index bc747ca0afde88..037a896fc7f070 100644 --- a/packages/edit-post/src/components/header/preferences-menu-item/index.js +++ b/packages/edit-post/src/components/header/preferences-menu-item/index.js @@ -4,18 +4,19 @@ import { useDispatch } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; import { MenuItem } from '@wordpress/components'; +import { store as interfaceStore } from '@wordpress/interface'; /** * Internal dependencies */ -import { store as editPostStore } from '../../../store'; +import { PREFERENCES_MODAL_NAME } from '../../../components/preferences-modal'; export default function PreferencesMenuItem() { - const { openModal } = useDispatch( editPostStore ); + const { openModal } = useDispatch( interfaceStore ); return ( { - openModal( 'edit-post/preferences' ); + openModal( PREFERENCES_MODAL_NAME ); } } > { __( 'Preferences' ) } diff --git a/packages/edit-post/src/components/keyboard-shortcut-help-modal/index.js b/packages/edit-post/src/components/keyboard-shortcut-help-modal/index.js index 54565bb5dcd5b5..9a7ce46704d479 100644 --- a/packages/edit-post/src/components/keyboard-shortcut-help-modal/index.js +++ b/packages/edit-post/src/components/keyboard-shortcut-help-modal/index.js @@ -14,6 +14,7 @@ import { } from '@wordpress/keyboard-shortcuts'; import { withSelect, withDispatch, useSelect } from '@wordpress/data'; import { compose } from '@wordpress/compose'; +import { store as interfaceStore } from '@wordpress/interface'; /** * Internal dependencies @@ -21,9 +22,9 @@ import { compose } from '@wordpress/compose'; import { textFormattingShortcuts } from './config'; import Shortcut from './shortcut'; import DynamicShortcut from './dynamic-shortcut'; -import { store as editPostStore } from '../../store'; -const MODAL_NAME = 'edit-post/keyboard-shortcut-help'; +export const KEYBOARD_SHORTCUT_HELP_MODAL_NAME = + 'edit-post/keyboard-shortcut-help'; const ShortcutList = ( { shortcuts } ) => ( /* @@ -141,14 +142,18 @@ export function KeyboardShortcutHelpModal( { isModalActive, toggleModal } ) { export default compose( [ withSelect( ( select ) => ( { - isModalActive: select( editPostStore ).isModalActive( MODAL_NAME ), + isModalActive: select( interfaceStore ).isModalActive( + KEYBOARD_SHORTCUT_HELP_MODAL_NAME + ), } ) ), withDispatch( ( dispatch, { isModalActive } ) => { - const { openModal, closeModal } = dispatch( editPostStore ); + const { openModal, closeModal } = dispatch( interfaceStore ); return { toggleModal: () => - isModalActive ? closeModal() : openModal( MODAL_NAME ), + isModalActive + ? closeModal() + : openModal( KEYBOARD_SHORTCUT_HELP_MODAL_NAME ), }; } ), ] )( KeyboardShortcutHelpModal ); diff --git a/packages/edit-post/src/components/preferences-modal/index.js b/packages/edit-post/src/components/preferences-modal/index.js index 77c6383b13f32c..bb0c8b5cd12715 100644 --- a/packages/edit-post/src/components/preferences-modal/index.js +++ b/packages/edit-post/src/components/preferences-modal/index.js @@ -18,6 +18,7 @@ import { PreferencesModal, PreferencesModalTabs, PreferencesModalSection, + store as interfaceStore, } from '@wordpress/interface'; import { store as preferencesStore } from '@wordpress/preferences'; @@ -35,17 +36,18 @@ import MetaBoxesSection from './meta-boxes-section'; import { store as editPostStore } from '../../store'; import BlockManager from '../block-manager'; -const MODAL_NAME = 'edit-post/preferences'; +export const PREFERENCES_MODAL_NAME = 'edit-post/preferences'; export default function EditPostPreferencesModal() { const isLargeViewport = useViewportMatch( 'medium' ); - const { closeModal } = useDispatch( editPostStore ); + const { closeModal } = useDispatch( interfaceStore ); const [ isModalActive, showBlockBreadcrumbsOption ] = useSelect( ( select ) => { const { getEditorSettings } = select( editorStore ); const { getEditorMode, isFeatureActive } = select( editPostStore ); - const modalActive = - select( editPostStore ).isModalActive( MODAL_NAME ); + const modalActive = select( interfaceStore ).isModalActive( + PREFERENCES_MODAL_NAME + ); const mode = getEditorMode(); const isRichEditingEnabled = getEditorSettings().richEditingEnabled; const isDistractionFreeEnabled = diff --git a/packages/edit-post/src/hooks/commands/use-common-commands.js b/packages/edit-post/src/hooks/commands/use-common-commands.js index 796e0665fc2fa3..0366e781799856 100644 --- a/packages/edit-post/src/hooks/commands/use-common-commands.js +++ b/packages/edit-post/src/hooks/commands/use-common-commands.js @@ -9,6 +9,7 @@ import { drawerLeft, drawerRight, blockDefault, + keyboardClose, } from '@wordpress/icons'; import { useCommand } from '@wordpress/commands'; import { store as preferencesStore } from '@wordpress/preferences'; @@ -17,11 +18,14 @@ import { store as interfaceStore } from '@wordpress/interface'; /** * Internal dependencies */ +import { KEYBOARD_SHORTCUT_HELP_MODAL_NAME } from '../../components/keyboard-shortcut-help-modal'; +import { PREFERENCES_MODAL_NAME } from '../../components/preferences-modal'; import { store as editPostStore } from '../../store'; export default function useCommonCommands() { const { openGeneralSidebar, closeGeneralSidebar, switchEditorMode } = useDispatch( editPostStore ); + const { openModal } = useDispatch( interfaceStore ); const { editorMode, activeSidebar } = useSelect( ( select ) => ( { activeSidebar: select( interfaceStore ).getActiveComplementaryArea( @@ -100,4 +104,22 @@ export default function useCommonCommands() { close(); }, } ); + + useCommand( { + name: 'core/open-preferences', + label: __( 'Open editor preferences' ), + icon: cog, + callback: () => { + openModal( PREFERENCES_MODAL_NAME ); + }, + } ); + + useCommand( { + name: 'core/open-shortcut-help', + label: __( 'Open keyboard shortcuts' ), + icon: keyboardClose, + callback: () => { + openModal( KEYBOARD_SHORTCUT_HELP_MODAL_NAME ); + }, + } ); } diff --git a/packages/edit-post/src/plugins/keyboard-shortcuts-help-menu-item/index.js b/packages/edit-post/src/plugins/keyboard-shortcuts-help-menu-item/index.js index 69f7b35c3f1167..930f420a241299 100644 --- a/packages/edit-post/src/plugins/keyboard-shortcuts-help-menu-item/index.js +++ b/packages/edit-post/src/plugins/keyboard-shortcuts-help-menu-item/index.js @@ -5,17 +5,18 @@ import { MenuItem } from '@wordpress/components'; import { withDispatch } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; import { displayShortcut } from '@wordpress/keycodes'; +import { store as interfaceStore } from '@wordpress/interface'; /** * Internal dependencies */ -import { store as editPostStore } from '../../store'; +import { KEYBOARD_SHORTCUT_HELP_MODAL_NAME } from '../../components/keyboard-shortcut-help-modal'; export function KeyboardShortcutsHelpMenuItem( { openModal } ) { return ( { - openModal( 'edit-post/keyboard-shortcut-help' ); + openModal( KEYBOARD_SHORTCUT_HELP_MODAL_NAME ); } } shortcut={ displayShortcut.access( 'h' ) } > @@ -25,7 +26,7 @@ export function KeyboardShortcutsHelpMenuItem( { openModal } ) { } export default withDispatch( ( dispatch ) => { - const { openModal } = dispatch( editPostStore ); + const { openModal } = dispatch( interfaceStore ); return { openModal, diff --git a/packages/edit-post/src/store/actions.js b/packages/edit-post/src/store/actions.js index 90f29f37b145cf..f938a9837516e0 100644 --- a/packages/edit-post/src/store/actions.js +++ b/packages/edit-post/src/store/actions.js @@ -10,6 +10,7 @@ import { store as noticesStore } from '@wordpress/notices'; import { store as coreStore } from '@wordpress/core-data'; import { store as blockEditorStore } from '@wordpress/block-editor'; import { store as editorStore } from '@wordpress/editor'; +import deprecated from '@wordpress/deprecated'; /** * Internal dependencies @@ -42,27 +43,39 @@ export const closeGeneralSidebar = /** * Returns an action object used in signalling that the user opened a modal. * + * @deprecated since WP 6.3 use `core/interface` store's action with the same name instead. + * + * * @param {string} name A string that uniquely identifies the modal. * * @return {Object} Action object. */ -export function openModal( name ) { - return { - type: 'OPEN_MODAL', - name, +export const openModal = + ( name ) => + ( { registry } ) => { + deprecated( "select( 'core/edit-post' ).openModal( name )", { + since: '6.3', + alternative: "select( 'core/interface').openModal( name )", + } ); + return registry.dispatch( interfaceStore ).openModal( name ); }; -} /** * Returns an action object signalling that the user closed a modal. * + * @deprecated since WP 6.3 use `core/interface` store's action with the same name instead. + * * @return {Object} Action object. */ -export function closeModal() { - return { - type: 'CLOSE_MODAL', +export const closeModal = + () => + ( { registry } ) => { + deprecated( "select( 'core/edit-post' ).closeModal()", { + since: '6.3', + alternative: "select( 'core/interface').closeModal()", + } ); + return registry.dispatch( interfaceStore ).closeModal(); }; -} /** * Returns an action object used in signalling that the user opened the publish diff --git a/packages/edit-post/src/store/reducer.js b/packages/edit-post/src/store/reducer.js index 8a4031ecf9b098..622b2e2667f7fc 100644 --- a/packages/edit-post/src/store/reducer.js +++ b/packages/edit-post/src/store/reducer.js @@ -22,25 +22,6 @@ export function removedPanels( state = [], action ) { return state; } -/** - * Reducer for storing the name of the open modal, or null if no modal is open. - * - * @param {Object} state Previous state. - * @param {Object} action Action object containing the `name` of the modal - * - * @return {Object} Updated state - */ -export function activeModal( state = null, action ) { - switch ( action.type ) { - case 'OPEN_MODAL': - return action.name; - case 'CLOSE_MODAL': - return null; - } - - return state; -} - export function publishSidebarActive( state = false, action ) { switch ( action.type ) { case 'OPEN_PUBLISH_SIDEBAR': @@ -209,7 +190,6 @@ const metaBoxes = combineReducers( { } ); export default combineReducers( { - activeModal, metaBoxes, publishSidebarActive, removedPanels, diff --git a/packages/edit-post/src/store/selectors.js b/packages/edit-post/src/store/selectors.js index b84e2b6887431a..5ae7d6b4437b71 100644 --- a/packages/edit-post/src/store/selectors.js +++ b/packages/edit-post/src/store/selectors.js @@ -298,14 +298,22 @@ export const isEditorPanelOpened = createRegistrySelector( /** * Returns true if a modal is active, or false otherwise. * + * @deprecated since WP 6.3 use `core/interface` store's selector with the same name instead. + * * @param {Object} state Global application state. * @param {string} modalName A string that uniquely identifies the modal. * * @return {boolean} Whether the modal is active. */ -export function isModalActive( state, modalName ) { - return state.activeModal === modalName; -} +export const isModalActive = createRegistrySelector( + ( select ) => ( state, modalName ) => { + deprecated( `select( 'core/edit-post' ).isModalActive`, { + since: '6.3', + alternative: `select( 'core/interface' ).isModalActive`, + } ); + return !! select( interfaceStore ).isModalActive( modalName ); + } +); /** * Returns whether the given feature is enabled or not. diff --git a/packages/edit-post/src/store/test/reducer.js b/packages/edit-post/src/store/test/reducer.js index 4be7c7cb3ffd80..a083de9c672868 100644 --- a/packages/edit-post/src/store/test/reducer.js +++ b/packages/edit-post/src/store/test/reducer.js @@ -7,7 +7,6 @@ import deepFreeze from 'deep-freeze'; * Internal dependencies */ import { - activeModal, isSavingMetaBoxes, metaBoxLocations, removedPanels, @@ -18,30 +17,6 @@ import { import { setIsInserterOpened, setIsListViewOpened } from '../actions'; describe( 'state', () => { - describe( 'activeModal', () => { - it( 'should default to null', () => { - const state = activeModal( undefined, {} ); - expect( state ).toBeNull(); - } ); - - it( 'should set the activeModal to the provided name', () => { - const state = activeModal( null, { - type: 'OPEN_MODAL', - name: 'test-modal', - } ); - - expect( state ).toEqual( 'test-modal' ); - } ); - - it( 'should set the activeModal to null', () => { - const state = activeModal( 'test-modal', { - type: 'CLOSE_MODAL', - } ); - - expect( state ).toBeNull(); - } ); - } ); - describe( 'isSavingMetaBoxes', () => { it( 'should return default state', () => { const actual = isSavingMetaBoxes( undefined, {} ); diff --git a/packages/edit-post/src/store/test/selectors.js b/packages/edit-post/src/store/test/selectors.js index 5df8456dd8534d..34c66ed7cf8e67 100644 --- a/packages/edit-post/src/store/test/selectors.js +++ b/packages/edit-post/src/store/test/selectors.js @@ -7,7 +7,6 @@ import deepFreeze from 'deep-freeze'; * Internal dependencies */ import { - isModalActive, hasMetaBoxes, isSavingMetaBoxes, getActiveMetaBoxLocations, @@ -18,32 +17,6 @@ import { } from '../selectors'; describe( 'selectors', () => { - describe( 'isModalActive', () => { - it( 'returns true if the provided name matches the value in the preferences activeModal property', () => { - const state = { - activeModal: 'test-modal', - }; - - expect( isModalActive( state, 'test-modal' ) ).toBe( true ); - } ); - - it( 'returns false if the provided name does not match the preferences activeModal property', () => { - const state = { - activeModal: 'something-else', - }; - - expect( isModalActive( state, 'test-modal' ) ).toBe( false ); - } ); - - it( 'returns false if the preferences activeModal property is null', () => { - const state = { - activeModal: null, - }; - - expect( isModalActive( state, 'test-modal' ) ).toBe( false ); - } ); - } ); - describe( 'isEditorPanelRemoved', () => { it( 'should return false by default', () => { const state = deepFreeze( { diff --git a/packages/edit-site/src/components/header-edit-mode/more-menu/index.js b/packages/edit-site/src/components/header-edit-mode/more-menu/index.js index 7fe63a343960ae..4b9d6c40fc6eec 100644 --- a/packages/edit-site/src/components/header-edit-mode/more-menu/index.js +++ b/packages/edit-site/src/components/header-edit-mode/more-menu/index.js @@ -2,13 +2,15 @@ * WordPress dependencies */ import { __, _x } from '@wordpress/i18n'; -import { useReducer } from '@wordpress/element'; import { useSelect, useDispatch, useRegistry } from '@wordpress/data'; -import { useShortcut } from '@wordpress/keyboard-shortcuts'; import { displayShortcut } from '@wordpress/keycodes'; import { external } from '@wordpress/icons'; import { MenuGroup, MenuItem, VisuallyHidden } from '@wordpress/components'; -import { ActionItem, MoreMenuDropdown } from '@wordpress/interface'; +import { + ActionItem, + MoreMenuDropdown, + store as interfaceStore, +} from '@wordpress/interface'; import { PreferenceToggleMenuItem, store as preferencesStore, @@ -17,8 +19,14 @@ import { /** * Internal dependencies */ -import KeyboardShortcutHelpModal from '../../keyboard-shortcut-help-modal'; -import EditSitePreferencesModal from '../../preferences-modal'; +import { + KEYBOARD_SHORTCUT_HELP_MODAL_NAME, + default as KeyboardShortcutHelpModal, +} from '../../keyboard-shortcut-help-modal'; +import { + PREFERENCES_MODAL_NAME, + default as EditSitePreferencesModal, +} from '../../preferences-modal'; import ToolsMoreMenuGroup from '../tools-more-menu-group'; import SiteExport from './site-export'; import WelcomeGuideMenuItem from './welcome-guide-menu-item'; @@ -27,16 +35,6 @@ import ModeSwitcher from '../mode-switcher'; import { store as siteEditorStore } from '../../../store'; export default function MoreMenu( { showIconLabels } ) { - const [ isModalActive, toggleModal ] = useReducer( - ( isActive ) => ! isActive, - false - ); - - const [ isPreferencesModalActive, togglePreferencesModal ] = useReducer( - ( isActive ) => ! isActive, - false - ); - const registry = useRegistry(); const isDistractionFree = useSelect( ( select ) => @@ -49,6 +47,7 @@ export default function MoreMenu( { showIconLabels } ) { const { setIsInserterOpened, setIsListViewOpened, closeGeneralSidebar } = useDispatch( siteEditorStore ); + const { openModal } = useDispatch( interfaceStore ); const { set: setPreference } = useDispatch( preferencesStore ); const toggleDistractionFree = () => { @@ -60,8 +59,6 @@ export default function MoreMenu( { showIconLabels } ) { } ); }; - useShortcut( 'core/edit-site/keyboard-shortcuts', toggleModal ); - return ( <> + openModal( + KEYBOARD_SHORTCUT_HELP_MODAL_NAME + ) + } shortcut={ displayShortcut.access( 'h' ) } > { __( 'Keyboard shortcuts' ) } @@ -156,21 +157,19 @@ export default function MoreMenu( { showIconLabels } ) { /> - + + openModal( PREFERENCES_MODAL_NAME ) + } + > { __( 'Preferences' ) } ) } - - + + ); } diff --git a/packages/edit-site/src/components/keyboard-shortcut-help-modal/index.js b/packages/edit-site/src/components/keyboard-shortcut-help-modal/index.js index aac95775cf1d0d..1da14a7cccbe77 100644 --- a/packages/edit-site/src/components/keyboard-shortcut-help-modal/index.js +++ b/packages/edit-site/src/components/keyboard-shortcut-help-modal/index.js @@ -8,8 +8,12 @@ import classnames from 'classnames'; */ import { Modal } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; -import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; -import { useSelect } from '@wordpress/data'; +import { + useShortcut, + store as keyboardShortcutsStore, +} from '@wordpress/keyboard-shortcuts'; +import { store as interfaceStore } from '@wordpress/interface'; +import { useSelect, useDispatch } from '@wordpress/data'; /** * Internal dependencies @@ -18,6 +22,9 @@ import { textFormattingShortcuts } from './config'; import Shortcut from './shortcut'; import DynamicShortcut from './dynamic-shortcut'; +export const KEYBOARD_SHORTCUT_HELP_MODAL_NAME = + 'edit-site/keyboard-shortcut-help'; + const ShortcutList = ( { shortcuts } ) => ( /* * Disable reason: The `list` ARIA role is redundant but @@ -82,14 +89,21 @@ const ShortcutCategorySection = ( { ); }; -export default function KeyboardShortcutHelpModal( { - isModalActive, - toggleModal, -} ) { +export default function KeyboardShortcutHelpModal() { + const isModalActive = useSelect( ( select ) => + select( interfaceStore ).isModalActive( + KEYBOARD_SHORTCUT_HELP_MODAL_NAME + ) + ); + const { closeModal, openModal } = useDispatch( interfaceStore ); + const toggleModal = () => + isModalActive + ? closeModal() + : openModal( KEYBOARD_SHORTCUT_HELP_MODAL_NAME ); + useShortcut( 'core/edit-site/keyboard-shortcuts', toggleModal ); if ( ! isModalActive ) { return null; } - return ( + select( interfaceStore ).isModalActive( PREFERENCES_MODAL_NAME ) + ); + const { closeModal, openModal } = useDispatch( interfaceStore ); + const toggleModal = () => + isModalActive ? closeModal() : openModal( PREFERENCES_MODAL_NAME ); const registry = useRegistry(); const { closeGeneralSidebar, setIsListViewOpened, setIsInserterOpened } = - useDispatch( siteEditorStore ); + useDispatch( editSiteStore ); const { set: setPreference } = useDispatch( preferencesStore ); const toggleDistractionFree = () => { diff --git a/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js b/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js index 2466bb2a706c4e..92ccee08592cde 100644 --- a/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js +++ b/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js @@ -4,8 +4,6 @@ import { useSelect, useDispatch } from '@wordpress/data'; import { __, isRTL } from '@wordpress/i18n'; import { - code, - cog, trash, backup, layout, @@ -13,6 +11,9 @@ import { drawerLeft, drawerRight, blockDefault, + cog, + code, + keyboardClose, } from '@wordpress/icons'; import { useCommandLoader } from '@wordpress/commands'; import { privateApis as routerPrivateApis } from '@wordpress/router'; @@ -26,6 +27,8 @@ import { store as editSiteStore } from '../../store'; import useEditedEntityRecord from '../../components/use-edited-entity-record'; import isTemplateRemovable from '../../utils/is-template-removable'; import isTemplateRevertable from '../../utils/is-template-revertable'; +import { KEYBOARD_SHORTCUT_HELP_MODAL_NAME } from '../../components/keyboard-shortcut-help-modal'; +import { PREFERENCES_MODAL_NAME } from '../../components/preferences-modal'; import { unlock } from '../../lock-unlock'; const { useHistory } = unlock( routerPrivateApis ); @@ -142,6 +145,7 @@ function useEditUICommands() { } ), [] ); + const { openModal } = useDispatch( interfaceStore ); const { toggle } = useDispatch( preferencesStore ); if ( canvasMode !== 'edit' ) { @@ -208,6 +212,24 @@ function useEditUICommands() { }, } ); + commands.push( { + name: 'core/open-preferences', + label: __( 'Open editor preferences' ), + icon: cog, + callback: () => { + openModal( PREFERENCES_MODAL_NAME ); + }, + } ); + + commands.push( { + name: 'core/open-shortcut-help', + label: __( 'Open keyboard shortcuts' ), + icon: keyboardClose, + callback: () => { + openModal( KEYBOARD_SHORTCUT_HELP_MODAL_NAME ); + }, + } ); + return { isLoading: false, commands, diff --git a/packages/interface/src/store/actions.js b/packages/interface/src/store/actions.js index bc51d4fca72202..b7a9935e888b51 100644 --- a/packages/interface/src/store/actions.js +++ b/packages/interface/src/store/actions.js @@ -181,3 +181,28 @@ export function setFeatureDefaults( scope, defaults ) { registry.dispatch( preferencesStore ).setDefaults( scope, defaults ); }; } + +/** + * Returns an action object used in signalling that the user opened a modal. + * + * @param {string} name A string that uniquely identifies the modal. + * + * @return {Object} Action object. + */ +export function openModal( name ) { + return { + type: 'OPEN_MODAL', + name, + }; +} + +/** + * Returns an action object signalling that the user closed a modal. + * + * @return {Object} Action object. + */ +export function closeModal() { + return { + type: 'CLOSE_MODAL', + }; +} diff --git a/packages/interface/src/store/reducer.js b/packages/interface/src/store/reducer.js index 433f71d15bcc57..8317e53cec95bf 100644 --- a/packages/interface/src/store/reducer.js +++ b/packages/interface/src/store/reducer.js @@ -30,6 +30,26 @@ export function complementaryAreas( state = {}, action ) { return state; } +/** + * Reducer for storing the name of the open modal, or null if no modal is open. + * + * @param {Object} state Previous state. + * @param {Object} action Action object containing the `name` of the modal + * + * @return {Object} Updated state + */ +export function activeModal( state = null, action ) { + switch ( action.type ) { + case 'OPEN_MODAL': + return action.name; + case 'CLOSE_MODAL': + return null; + } + + return state; +} + export default combineReducers( { complementaryAreas, + activeModal, } ); diff --git a/packages/interface/src/store/selectors.js b/packages/interface/src/store/selectors.js index c92e45bbd3c590..548cb2f70346ac 100644 --- a/packages/interface/src/store/selectors.js +++ b/packages/interface/src/store/selectors.js @@ -90,3 +90,15 @@ export const isFeatureActive = createRegistrySelector( return !! select( preferencesStore ).get( scope, featureName ); } ); + +/** + * Returns true if a modal is active, or false otherwise. + * + * @param {Object} state Global application state. + * @param {string} modalName A string that uniquely identifies the modal. + * + * @return {boolean} Whether the modal is active. + */ +export function isModalActive( state, modalName ) { + return state.activeModal === modalName; +} diff --git a/packages/interface/src/store/test/reducer.js b/packages/interface/src/store/test/reducer.js new file mode 100644 index 00000000000000..eb9424637bca0f --- /dev/null +++ b/packages/interface/src/store/test/reducer.js @@ -0,0 +1,30 @@ +/** + * Internal dependencies + */ +import { activeModal } from '../reducer'; + +describe( 'state', () => { + describe( 'activeModal', () => { + it( 'should default to null', () => { + const state = activeModal( undefined, {} ); + expect( state ).toBeNull(); + } ); + + it( 'should set the activeModal to the provided name', () => { + const state = activeModal( null, { + type: 'OPEN_MODAL', + name: 'test-modal', + } ); + + expect( state ).toEqual( 'test-modal' ); + } ); + + it( 'should set the activeModal to null', () => { + const state = activeModal( 'test-modal', { + type: 'CLOSE_MODAL', + } ); + + expect( state ).toBeNull(); + } ); + } ); +} ); diff --git a/packages/interface/src/store/test/selectors.js b/packages/interface/src/store/test/selectors.js new file mode 100644 index 00000000000000..a3bea882043d29 --- /dev/null +++ b/packages/interface/src/store/test/selectors.js @@ -0,0 +1,32 @@ +/** + * Internal dependencies + */ +import { isModalActive } from '../selectors'; + +describe( 'selectors', () => { + describe( 'isModalActive', () => { + it( 'returns true if the provided name matches the value in the preferences activeModal property', () => { + const state = { + activeModal: 'test-modal', + }; + + expect( isModalActive( state, 'test-modal' ) ).toBe( true ); + } ); + + it( 'returns false if the provided name does not match the preferences activeModal property', () => { + const state = { + activeModal: 'something-else', + }; + + expect( isModalActive( state, 'test-modal' ) ).toBe( false ); + } ); + + it( 'returns false if the preferences activeModal property is null', () => { + const state = { + activeModal: null, + }; + + expect( isModalActive( state, 'test-modal' ) ).toBe( false ); + } ); + } ); +} ); From 458ed04c038c9455bc15f8f23fe08476c512e964 Mon Sep 17 00:00:00 2001 From: Ramon Date: Wed, 28 Jun 2023 00:39:34 +1000 Subject: [PATCH 035/266] Blocks: remove gutenberg refs in PHP files (#51978) * Removes references to execution code referencing gutenberg where possible to avoid conflicts with Core. * Added deprecated notices * Removed `gutenberg_` for `IS_GUTENBERG_PLUGIN` only method --- .../block-library/src/latest-posts/index.php | 2 +- .../block-library/src/navigation/index.php | 34 +++++++++++++------ 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/packages/block-library/src/latest-posts/index.php b/packages/block-library/src/latest-posts/index.php index 746133ec7a8989..c7a37fe0fe31f5 100644 --- a/packages/block-library/src/latest-posts/index.php +++ b/packages/block-library/src/latest-posts/index.php @@ -51,7 +51,7 @@ function render_block_core_latest_posts( $attributes ) { $filter_latest_posts_excerpt_more = static function( $more ) use ( $attributes ) { $use_excerpt = 'excerpt' === $attributes['displayPostContentRadio']; /* translators: %1$s is a URL to a post, excerpt truncation character, default … */ - return $use_excerpt ? sprintf( __( ' … Read more', 'gutenberg' ), esc_url( get_permalink() ) ) : $more; + return $use_excerpt ? sprintf( __( ' … Read more' ), esc_url( get_permalink() ) ) : $more; }; add_filter( 'excerpt_more', $filter_latest_posts_excerpt_more ); diff --git a/packages/block-library/src/navigation/index.php b/packages/block-library/src/navigation/index.php index d16de821b47125..6de09f8dd36aa9 100644 --- a/packages/block-library/src/navigation/index.php +++ b/packages/block-library/src/navigation/index.php @@ -94,7 +94,7 @@ function block_core_navigation_sort_menu_items_by_parent_id( $menu_items ) { * * @return string Submenu markup with the directives injected. */ - function gutenberg_block_core_navigation_add_directives_to_submenu( $w, $block_attributes ) { + function block_core_navigation_add_directives_to_submenu( $w, $block_attributes ) { while ( $w->next_tag( array( 'tag_name' => 'LI', @@ -124,7 +124,7 @@ function gutenberg_block_core_navigation_add_directives_to_submenu( $w, $block_a }; // Iterate through subitems if exist. - gutenberg_block_core_navigation_add_directives_to_submenu( $w, $block_attributes ); + block_core_navigation_add_directives_to_submenu( $w, $block_attributes ); } return $w->get_updated_html(); }; @@ -341,7 +341,11 @@ function block_core_navigation_get_fallback_blocks() { // If `core/page-list` is not registered then return empty blocks. $fallback_blocks = $registry->is_registered( 'core/page-list' ) ? $page_list_fallback : array(); - $navigation_post = Gutenberg_Navigation_Fallback::get_fallback(); + if ( class_exists( 'WP_Navigation_Fallback' ) ) { + $navigation_post = WP_Navigation_Fallback::get_fallback(); + } else { + $navigation_post = Gutenberg_Navigation_Fallback::get_fallback(); + } // Use the first non-empty Navigation as fallback if available. if ( $navigation_post ) { @@ -673,7 +677,7 @@ function render_block_core_navigation( $attributes, $content, $block ) { // Add directives to the submenu if needed. if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN && $has_submenus && $should_load_view_script ) { $w = new WP_HTML_Tag_Processor( $inner_blocks_html ); - $inner_blocks_html = gutenberg_block_core_navigation_add_directives_to_submenu( $w, $attributes ); + $inner_blocks_html = block_core_navigation_add_directives_to_submenu( $w, $attributes ); } $modal_unique_id = wp_unique_id( 'modal-' ); @@ -836,6 +840,8 @@ function block_core_navigation_typographic_presets_backcompatibility( $parsed_bl /** * Turns menu item data into a nested array of parsed blocks * + * @deprecated 6.3.0 Use WP_Navigation_Fallback::parse_blocks_from_menu_items() instead. + * * @param array $menu_items An array of menu items that represent * an individual level of a menu. * @param array $menu_items_by_parent_id An array keyed by the id of the @@ -846,7 +852,7 @@ function block_core_navigation_typographic_presets_backcompatibility( $parsed_bl */ function block_core_navigation_parse_blocks_from_menu_items( $menu_items, $menu_items_by_parent_id ) { - _deprecated_function( __FUNCTION__, '6.3.0', 'Gutenberg_Navigation_Fallback::parse_blocks_from_menu_items' ); + _deprecated_function( __FUNCTION__, '6.3.0', 'WP_Navigation_Fallback::parse_blocks_from_menu_items' ); if ( empty( $menu_items ) ) { return array(); @@ -891,11 +897,13 @@ function block_core_navigation_parse_blocks_from_menu_items( $menu_items, $menu_ /** * Get the classic navigation menu to use as a fallback. * + * @deprecated 6.3.0 Use WP_Navigation_Fallback::get_classic_menu_fallback() instead. + * * @return object WP_Term The classic navigation. */ function block_core_navigation_get_classic_menu_fallback() { - _deprecated_function( __FUNCTION__, '6.3.0', 'Gutenberg_Navigation_Fallback::get_classic_menu_fallback' ); + _deprecated_function( __FUNCTION__, '6.3.0', 'WP_Navigation_Fallback::get_classic_menu_fallback' ); $classic_nav_menus = wp_get_nav_menus(); @@ -933,12 +941,14 @@ static function( $a, $b ) { /** * Converts a classic navigation to blocks. * + * @deprecated 6.3.0 Use WP_Navigation_Fallback::get_classic_menu_fallback_blocks() instead. + * * @param object $classic_nav_menu WP_Term The classic navigation object to convert. * @return array the normalized parsed blocks. */ function block_core_navigation_get_classic_menu_fallback_blocks( $classic_nav_menu ) { - _deprecated_function( __FUNCTION__, '6.3.0', 'Gutenberg_Navigation_Fallback::get_classic_menu_fallback_blocks' ); + _deprecated_function( __FUNCTION__, '6.3.0', 'WP_Navigation_Fallback::get_classic_menu_fallback_blocks' ); // BEGIN: Code that already exists in wp_nav_menu(). $menu_items = wp_get_nav_menu_items( $classic_nav_menu->term_id, array( 'update_post_term_cache' => false ) ); @@ -971,13 +981,15 @@ function block_core_navigation_get_classic_menu_fallback_blocks( $classic_nav_me } /** - * If there's a the classic menu then use it as a fallback. + * If there's a classic menu then use it as a fallback. + * + * @deprecated 6.3.0 Use WP_Navigation_Fallback::create_classic_menu_fallback() instead. * * @return array the normalized parsed blocks. */ function block_core_navigation_maybe_use_classic_menu_fallback() { - _deprecated_function( __FUNCTION__, '6.3.0', 'Gutenberg_Navigation_Fallback::create_classic_menu_fallback' ); + _deprecated_function( __FUNCTION__, '6.3.0', 'WP_Navigation_Fallback::create_classic_menu_fallback' ); // See if we have a classic menu. $classic_nav_menu = block_core_navigation_get_classic_menu_fallback(); @@ -1016,11 +1028,13 @@ function block_core_navigation_maybe_use_classic_menu_fallback() { /** * Finds the most recently published `wp_navigation` Post. * + * @deprecated 6.3.0 Use WP_Navigation_Fallback::get_most_recently_published_navigation() instead. + * * @return WP_Post|null the first non-empty Navigation or null. */ function block_core_navigation_get_most_recently_published_navigation() { - _deprecated_function( __FUNCTION__, '6.3.0', 'Gutenberg_Navigation_Fallback::get_most_recently_published_navigation' ); + _deprecated_function( __FUNCTION__, '6.3.0', 'WP_Navigation_Fallback::get_most_recently_published_navigation' ); // Default to the most recently created menu. $parsed_args = array( From dd145fc710282af87cbd21b22b99f2911ef899d9 Mon Sep 17 00:00:00 2001 From: James Koster Date: Tue, 27 Jun 2023 15:54:55 +0100 Subject: [PATCH 036/266] Try: Update template titles (#51428) * Update template titles * Fix typo Co-authored-by: Alex Stine * Revert Index rename * "front page" -> "homepage" * Update 404 Page make more sense given the template appears in the Pages panel too. * Front Page * home title + description * Revert Home name change, and move compat files * separate code for wp versions * update tests --------- Co-authored-by: Alex Stine Co-authored-by: ntsekouras --- .../wordpress-6.2/block-template-utils.php | 145 +---------------- .../wordpress-6.3/block-template-utils.php | 150 ++++++++++++++++++ lib/load.php | 5 +- .../blocks/post-comments-form.test.js | 2 +- .../site-editor/settings-sidebar.test.js | 2 +- .../various/post-editor-template-mode.spec.js | 2 +- 6 files changed, 156 insertions(+), 150 deletions(-) create mode 100644 lib/compat/wordpress-6.3/block-template-utils.php diff --git a/lib/compat/wordpress-6.2/block-template-utils.php b/lib/compat/wordpress-6.2/block-template-utils.php index 7611b9be5cf1bb..db9bf427e6b471 100644 --- a/lib/compat/wordpress-6.2/block-template-utils.php +++ b/lib/compat/wordpress-6.2/block-template-utils.php @@ -1,7 +1,7 @@ _x( 'Index', 'Template name', 'gutenberg' ), - 'description' => __( - 'Used as a fallback template for all pages when a more specific template is not defined.', - 'gutenberg' - ), - ); - } - if ( isset( $default_template_types['home'] ) ) { - $default_template_types['home'] = array( - 'title' => _x( 'Home', 'Template name', 'gutenberg' ), - 'description' => __( - 'Displays the latest posts as either the site homepage or a custom page defined under reading settings. If it exists, the Front Page template overrides this template when posts are shown on the front page.', - 'gutenberg' - ), - ); - } - if ( isset( $default_template_types['front-page'] ) ) { - $default_template_types['front-page'] = array( - 'title' => _x( 'Front Page', 'Template name', 'gutenberg' ), - 'description' => __( - "Displays your site's front page, whether it is set to display latest posts or a static page. The Front Page template takes precedence over all templates.", - 'gutenberg' - ), - ); - } - if ( isset( $default_template_types['singular'] ) ) { - $default_template_types['singular'] = array( - 'title' => _x( 'Singular', 'Template name', 'gutenberg' ), - 'description' => __( - 'Displays any single entry, such as a post or a page. This template will serve as a fallback when a more specific template (e.g., Single Post, Page, or Attachment) cannot be found.', - 'gutenberg' - ), - ); - } - if ( isset( $default_template_types['single'] ) ) { - $default_template_types['single'] = array( - 'title' => _x( 'Single', 'Template name', 'gutenberg' ), - 'description' => __( 'Displays single posts on your website unless a custom template has been applied to that post or a dedicated template exists.', 'gutenberg' ), - ); - } - if ( isset( $default_template_types['page'] ) ) { - $default_template_types['page'] = array( - 'title' => _x( 'Page', 'Template name', 'gutenberg' ), - 'description' => __( 'Display all static pages unless a custom template has been applied or a dedicated template exists.', 'gutenberg' ), - ); - } - if ( isset( $default_template_types['archive'] ) ) { - $default_template_types['archive'] = array( - 'title' => _x( 'Archive', 'Template name', 'gutenberg' ), - 'description' => __( - 'Displays any archive, including posts by a single author, category, tag, taxonomy, custom post type, and date. This template will serve as a fallback when more specific templates (e.g., Category or Tag) cannot be found.', - 'gutenberg' - ), - ); - } - if ( isset( $default_template_types['author'] ) ) { - $default_template_types['author'] = array( - 'title' => _x( 'Author', 'Template name', 'gutenberg' ), - 'description' => __( - "Displays a single author's post archive. This template will serve as a fallback when a more a specific template (e.g., Author: Admin) cannot be found.", - 'gutenberg' - ), - ); - } - if ( isset( $default_template_types['category'] ) ) { - $default_template_types['category'] = array( - 'title' => _x( 'Category', 'Template name', 'gutenberg' ), - 'description' => __( - 'Displays a post category archive. This template will serve as a fallback when more specific template (e.g., Category: Recipes) cannot be found.', - 'gutenberg' - ), - ); - } - if ( isset( $default_template_types['taxonomy'] ) ) { - $default_template_types['taxonomy'] = array( - 'title' => _x( 'Taxonomy', 'Template name', 'gutenberg' ), - 'description' => __( - 'Displays a custom taxonomy archive. Like categories and tags, taxonomies have terms which you use to classify things. For example: a taxonomy named "Art" can have multiple terms, such as "Modern" and "18th Century." This template will serve as a fallback when a more specific template (e.g, Taxonomy: Art) cannot be found.', - 'gutenberg' - ), - ); - } - if ( isset( $default_template_types['date'] ) ) { - $default_template_types['date'] = array( - 'title' => _x( 'Date', 'Template name', 'gutenberg' ), - 'description' => __( 'Displays a post archive when a specific date is visited (e.g., example.com/2023/).', 'gutenberg' ), - ); - } - if ( isset( $default_template_types['tag'] ) ) { - $default_template_types['tag'] = array( - 'title' => _x( 'Tag', 'Template name', 'gutenberg' ), - 'description' => __( - 'Displays a post tag archive. This template will serve as a fallback when more specific template (e.g., Tag: Pizza) cannot be found.', - 'gutenberg' - ), - ); - } - if ( isset( $default_template_types['attachment'] ) ) { - $default_template_types['attachment'] = array( - 'title' => _x( 'Media', 'Template name', 'gutenberg' ), - 'description' => __( 'Displays when a visitor views the dedicated page that exists for any media attachment.', 'gutenberg' ), - ); - } - if ( isset( $default_template_types['search'] ) ) { - $default_template_types['search'] = array( - 'title' => _x( 'Search', 'Template name', 'gutenberg' ), - 'description' => __( 'Displays when a visitor performs a search on your website.', 'gutenberg' ), - ); - } - if ( isset( $default_template_types['privacy-policy'] ) ) { - $default_template_types['privacy-policy'] = array( - 'title' => _x( 'Privacy Policy', 'Template name', 'gutenberg' ), - 'description' => __( - "Displays your site's Privacy Policy page.", - 'gutenberg' - ), - ); - } - if ( isset( $default_template_types['404'] ) ) { - $default_template_types['404'] = array( - 'title' => _x( '404', 'Template name', 'gutenberg' ), - 'description' => __( 'Displays when a visitor views a non-existent page, such as a dead link or a mistyped URL.', 'gutenberg' ), - ); - } - - return $default_template_types; -} -add_filter( 'default_template_types', 'gutenberg_get_default_block_template_types', 10 ); diff --git a/lib/compat/wordpress-6.3/block-template-utils.php b/lib/compat/wordpress-6.3/block-template-utils.php new file mode 100644 index 00000000000000..cdd870db2d0813 --- /dev/null +++ b/lib/compat/wordpress-6.3/block-template-utils.php @@ -0,0 +1,150 @@ + _x( 'Index', 'Template name', 'gutenberg' ), + 'description' => __( + 'Used as a fallback template for all pages when a more specific template is not defined.', + 'gutenberg' + ), + ); + } + if ( isset( $default_template_types['home'] ) ) { + $default_template_types['home'] = array( + 'title' => _x( 'Home', 'Template name', 'gutenberg' ), + 'description' => __( + 'Displays the latest posts as either the site homepage or as the "Posts page" as defined under reading settings. If it exists, the Front Page template overrides this template when posts are shown on the homepage.', + 'gutenberg' + ), + ); + } + if ( isset( $default_template_types['front-page'] ) ) { + $default_template_types['front-page'] = array( + 'title' => _x( 'Front Page', 'Template name', 'gutenberg' ), + 'description' => __( + "Displays your site's homepage, whether it is set to display latest posts or a static page. The Front Page template takes precedence over all templates.", + 'gutenberg' + ), + ); + } + if ( isset( $default_template_types['singular'] ) ) { + $default_template_types['singular'] = array( + 'title' => _x( 'Single Entries', 'Template name', 'gutenberg' ), + 'description' => __( + 'Displays any single entry, such as a post or a page. This template will serve as a fallback when a more specific template (e.g., Single Post, Page, or Attachment) cannot be found.', + 'gutenberg' + ), + ); + } + if ( isset( $default_template_types['single'] ) ) { + $default_template_types['single'] = array( + 'title' => _x( 'Single Posts', 'Template name', 'gutenberg' ), + 'description' => __( 'Displays single posts on your website unless a custom template has been applied to that post or a dedicated template exists.', 'gutenberg' ), + ); + } + if ( isset( $default_template_types['page'] ) ) { + $default_template_types['page'] = array( + 'title' => _x( 'Pages', 'Template name', 'gutenberg' ), + 'description' => __( 'Display all static pages unless a custom template has been applied or a dedicated template exists.', 'gutenberg' ), + ); + } + if ( isset( $default_template_types['archive'] ) ) { + $default_template_types['archive'] = array( + 'title' => _x( 'All Archives', 'Template name', 'gutenberg' ), + 'description' => __( + 'Displays any archive, including posts by a single author, category, tag, taxonomy, custom post type, and date. This template will serve as a fallback when more specific templates (e.g., Category or Tag) cannot be found.', + 'gutenberg' + ), + ); + } + if ( isset( $default_template_types['author'] ) ) { + $default_template_types['author'] = array( + 'title' => _x( 'Author Archives', 'Template name', 'gutenberg' ), + 'description' => __( + "Displays a single author's post archive. This template will serve as a fallback when a more a specific template (e.g., Author: Admin) cannot be found.", + 'gutenberg' + ), + ); + } + if ( isset( $default_template_types['category'] ) ) { + $default_template_types['category'] = array( + 'title' => _x( 'Category Archives', 'Template name', 'gutenberg' ), + 'description' => __( + 'Displays a post category archive. This template will serve as a fallback when more specific template (e.g., Category: Recipes) cannot be found.', + 'gutenberg' + ), + ); + } + if ( isset( $default_template_types['taxonomy'] ) ) { + $default_template_types['taxonomy'] = array( + 'title' => _x( 'Taxonomy', 'Template name', 'gutenberg' ), + 'description' => __( + 'Displays a custom taxonomy archive. Like categories and tags, taxonomies have terms which you use to classify things. For example: a taxonomy named "Art" can have multiple terms, such as "Modern" and "18th Century." This template will serve as a fallback when a more specific template (e.g, Taxonomy: Art) cannot be found.', + 'gutenberg' + ), + ); + } + if ( isset( $default_template_types['date'] ) ) { + $default_template_types['date'] = array( + 'title' => _x( 'Date Archives', 'Template name', 'gutenberg' ), + 'description' => __( 'Displays a post archive when a specific date is visited (e.g., example.com/2023/).', 'gutenberg' ), + ); + } + if ( isset( $default_template_types['tag'] ) ) { + $default_template_types['tag'] = array( + 'title' => _x( 'Tag Archives', 'Template name', 'gutenberg' ), + 'description' => __( + 'Displays a post tag archive. This template will serve as a fallback when more specific template (e.g., Tag: Pizza) cannot be found.', + 'gutenberg' + ), + ); + } + if ( isset( $default_template_types['attachment'] ) ) { + $default_template_types['attachment'] = array( + 'title' => _x( 'Attachment Pages', 'Template name', 'gutenberg' ), + 'description' => __( 'Displays when a visitor views the dedicated page that exists for any media attachment.', 'gutenberg' ), + ); + } + if ( isset( $default_template_types['search'] ) ) { + $default_template_types['search'] = array( + 'title' => _x( 'Search Results', 'Template name', 'gutenberg' ), + 'description' => __( 'Displays when a visitor performs a search on your website.', 'gutenberg' ), + ); + } + if ( isset( $default_template_types['privacy-policy'] ) ) { + $default_template_types['privacy-policy'] = array( + 'title' => _x( 'Privacy Policy', 'Template name', 'gutenberg' ), + 'description' => __( + "Displays your site's Privacy Policy page.", + 'gutenberg' + ), + ); + } + if ( isset( $default_template_types['404'] ) ) { + $default_template_types['404'] = array( + 'title' => _x( 'Page: 404', 'Template name', 'gutenberg' ), + 'description' => __( 'Displays when a visitor views a non-existent page, such as a dead link or a mistyped URL.', 'gutenberg' ), + ); + } + + return $default_template_types; +} +add_filter( 'default_template_types', 'gutenberg_get_default_block_template_types', 10 ); diff --git a/lib/load.php b/lib/load.php index a390263fa119da..735d0d678d2811 100644 --- a/lib/load.php +++ b/lib/load.php @@ -83,9 +83,6 @@ function gutenberg_is_experiment_enabled( $name ) { require __DIR__ . '/compat/wordpress-6.2/widgets.php'; require __DIR__ . '/compat/wordpress-6.2/menu.php'; -// WordPress 6.3 compat. -require __DIR__ . '/compat/wordpress-6.3/get-global-styles-and-settings.php'; - if ( ! class_exists( 'WP_HTML_Tag_Processor' ) ) { require __DIR__ . '/compat/wordpress-6.2/html-api/class-wp-html-attribute-token.php'; require __DIR__ . '/compat/wordpress-6.2/html-api/class-wp-html-span.php'; @@ -94,6 +91,8 @@ function gutenberg_is_experiment_enabled( $name ) { } // WordPress 6.3 compat. +require __DIR__ . '/compat/wordpress-6.3/get-global-styles-and-settings.php'; +require __DIR__ . '/compat/wordpress-6.3/block-template-utils.php'; require __DIR__ . '/compat/wordpress-6.3/html-api/class-gutenberg-html-tag-processor-6-3.php'; require __DIR__ . '/compat/wordpress-6.3/script-loader.php'; require __DIR__ . '/compat/wordpress-6.3/blocks.php'; diff --git a/packages/e2e-tests/specs/experiments/blocks/post-comments-form.test.js b/packages/e2e-tests/specs/experiments/blocks/post-comments-form.test.js index 9d395707e382f3..3a561ad1e2cb6c 100644 --- a/packages/e2e-tests/specs/experiments/blocks/post-comments-form.test.js +++ b/packages/e2e-tests/specs/experiments/blocks/post-comments-form.test.js @@ -37,7 +37,7 @@ describe( 'Post Comments Form', () => { ); await expect( page ).toClick( '.edit-site-sidebar-navigation-item', - { text: /singular/i } + { text: /single entries/i } ); await enterEditMode(); diff --git a/packages/e2e-tests/specs/site-editor/settings-sidebar.test.js b/packages/e2e-tests/specs/site-editor/settings-sidebar.test.js index ae28019cf0d992..8bd33d7138fc95 100644 --- a/packages/e2e-tests/specs/site-editor/settings-sidebar.test.js +++ b/packages/e2e-tests/specs/site-editor/settings-sidebar.test.js @@ -79,7 +79,7 @@ describe( 'Settings sidebar', () => { 'Used as a fallback template for all pages when a more specific template is not defined.', } ); expect( templateCardAfterNavigation ).toMatchObject( { - title: 'Singular', + title: 'Single Entries', description: 'Displays any single entry, such as a post or a page. This template will serve as a fallback when a more specific template (e.g., Single Post, Page, or Attachment) cannot be found.', } ); diff --git a/test/e2e/specs/editor/various/post-editor-template-mode.spec.js b/test/e2e/specs/editor/various/post-editor-template-mode.spec.js index 9689f3af54b422..a8e4d04df378e1 100644 --- a/test/e2e/specs/editor/various/post-editor-template-mode.spec.js +++ b/test/e2e/specs/editor/various/post-editor-template-mode.spec.js @@ -151,7 +151,7 @@ class PostEditorTemplateMode { await expect( this.editorTopBar.getByRole( 'heading[level=1]' ) - ).toHaveText( 'Editing template: Singular' ); + ).toHaveText( 'Editing template: Single Entries' ); } async createPostAndSaveDraft() { From d2a0b393019bf99e645cfa31721c383264fcf4a3 Mon Sep 17 00:00:00 2001 From: Marin Atanasov <8436925+tyxla@users.noreply.github.com> Date: Tue, 27 Jun 2023 17:59:49 +0300 Subject: [PATCH 037/266] Lodash: Remove from block library package (#51976) --- package-lock.json | 1 - packages/block-library/package.json | 1 - 2 files changed, 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 07bfd23f236659..4de1bfc12b7036 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17340,7 +17340,6 @@ "escape-html": "^1.0.3", "fast-average-color": "^9.1.1", "fast-deep-equal": "^3.1.3", - "lodash": "^4.17.21", "memize": "^2.1.0", "micromodal": "^0.4.10", "preact": "^10.13.2", diff --git a/packages/block-library/package.json b/packages/block-library/package.json index 8d145f51d4a760..bd1c9d69828313 100644 --- a/packages/block-library/package.json +++ b/packages/block-library/package.json @@ -69,7 +69,6 @@ "escape-html": "^1.0.3", "fast-average-color": "^9.1.1", "fast-deep-equal": "^3.1.3", - "lodash": "^4.17.21", "memize": "^2.1.0", "micromodal": "^0.4.10", "preact": "^10.13.2", From 72b7d89dbcda2ba63bc1ad99e8fc5f33309401d3 Mon Sep 17 00:00:00 2001 From: Bart Kalisz Date: Tue, 27 Jun 2023 17:09:47 +0200 Subject: [PATCH 038/266] Migrate performance tests to Playwright (#51084) --- .eslintrc.js | 5 +- package.json | 1 + test/performance/assets/large-post.html | 5553 +++++++++++++++++ .../assets/small-post-with-containers.html | 77 + test/performance/config/global-setup.ts | 31 + .../config/performance-reporter.ts | 174 + test/performance/playwright.config.ts | 64 + .../specs/front-end-block-theme.spec.js | 76 + .../specs/front-end-classic-theme.spec.js | 75 + test/performance/specs/post-editor.spec.js | 390 ++ test/performance/specs/site-editor.spec.js | 206 + test/performance/tsconfig.json | 11 + test/performance/utils.js | 183 + 13 files changed, 6845 insertions(+), 1 deletion(-) create mode 100644 test/performance/assets/large-post.html create mode 100644 test/performance/assets/small-post-with-containers.html create mode 100644 test/performance/config/global-setup.ts create mode 100644 test/performance/config/performance-reporter.ts create mode 100644 test/performance/playwright.config.ts create mode 100644 test/performance/specs/front-end-block-theme.spec.js create mode 100644 test/performance/specs/front-end-classic-theme.spec.js create mode 100644 test/performance/specs/post-editor.spec.js create mode 100644 test/performance/specs/site-editor.spec.js create mode 100644 test/performance/tsconfig.json create mode 100644 test/performance/utils.js diff --git a/.eslintrc.js b/.eslintrc.js index 18763807cb36be..2b31cc509dd2cd 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -367,7 +367,7 @@ module.exports = { }, { files: [ 'packages/jest*/**/*.js', '**/test/**/*.js' ], - excludedFiles: [ 'test/e2e/**/*.js' ], + excludedFiles: [ 'test/e2e/**/*.js', 'test/performance/**/*.js' ], extends: [ 'plugin:@wordpress/eslint-plugin/test-unit' ], }, { @@ -377,6 +377,7 @@ module.exports = { 'packages/react-native-*/**/*.[tj]s?(x)', 'test/native/**/*.[tj]s?(x)', 'test/e2e/**/*.[tj]s?(x)', + 'test/performance/**/*.[tj]s?(x)', 'test/storybook-playwright/**/*.[tj]s?(x)', ], extends: [ @@ -396,6 +397,7 @@ module.exports = { { files: [ 'test/e2e/**/*.[tj]s', + 'test/performance/**/*.[tj]s', 'packages/e2e-test-utils-playwright/**/*.[tj]s', ], extends: [ @@ -406,6 +408,7 @@ module.exports = { tsconfigRootDir: __dirname, project: [ './test/e2e/tsconfig.json', + './test/performance/tsconfig.json', './packages/e2e-test-utils-playwright/tsconfig.json', ], }, diff --git a/package.json b/package.json index a061969e8817e9..8910f972fe91b0 100644 --- a/package.json +++ b/package.json @@ -311,6 +311,7 @@ "test:e2e:watch": "npm run test:e2e -- --watch", "test:performance": "wp-scripts test-e2e --config packages/e2e-tests/jest.performance.config.js", "test:performance:debug": "wp-scripts --inspect-brk test-e2e --runInBand --no-cache --verbose --config packages/e2e-tests/jest.performance.config.js --puppeteer-devtools", + "test:performance:playwright": "playwright test --config test/performance/playwright.config.ts", "test:php": "npm-run-all lint:php test:unit:php", "test:php:watch": "wp-env run --env-cwd='wp-content/plugins/gutenberg' tests-cli composer run-script test:watch", "test:unit": "wp-scripts test-unit-js --config test/unit/jest.config.js", diff --git a/test/performance/assets/large-post.html b/test/performance/assets/large-post.html new file mode 100644 index 00000000000000..0103e22c89c3b1 --- /dev/null +++ b/test/performance/assets/large-post.html @@ -0,0 +1,5553 @@ + +

At iam decimum annum in spelunca iacet.

+ + + +

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sit enim idem caecus, debilis. Potius inflammat, ut coercendi magis quam dedocendi esse videantur. Quis non odit sordidos, vanos, leves, futtiles? Age sane, inquam. Duo Reges: constructio interrete. Nam ante Aristippus, et ille melius. Sed quamvis comis in amicis tuendis fuerit, tamen, si haec vera sunt-nihil enim affirmo-, non satis acutus fuit.

+ + + +
    +
  • At habetur! Et ego id scilicet nesciebam! Sed ut sit, etiamne post mortem coletur?
  • + + + +
  • Totum genus hoc Zeno et qui ab eo sunt aut non potuerunt aut noluerunt, certe reliquerunt.
  • + + + +
  • At quicum ioca seria, ut dicitur, quicum arcana, quicum occulta omnia?
  • +
+ + + +

Non igitur bene. Quod si ita se habeat, non possit beatam praestare vitam sapientia. Universa enim illorum ratione cum tota vestra confligendum puto. Atqui reperies, inquit, in hoc quidem pertinacem; Haec para/doca illi, nos admirabilia dicamus. Quod cum ille dixisset et satis disputatum videretur, in oppidum ad Pomponium perreximus omnes. Estne, quaeso, inquam, sitienti in bibendo voluptas? Egone non intellego, quid sit don Graece, Latine voluptas?

+ + + +

Nummus in Croesi divitiis obscuratur, pars est tamen divitiarum.

+ + + +

Varietates autem iniurasque fortunae facile veteres philosophorum praeceptis instituta vita superabat. De vacuitate doloris eadem sententia erit. Ratio quidem vestra sic cogit. Sit, inquam, tam facilis, quam vultis, comparatio voluptatis, quid de dolore dicemus? Si longus, levis dictata sunt. Sed ea mala virtuti magnitudine obruebantur. Non quam nostram quidem, inquit Pomponius iocans; Tecum optime, deinde etiam cum mediocri amico. At ille pellit, qui permulcet sensum voluptate.

+ + + +

Haec quo modo conveniant, non sane intellego. Quid, quod homines infima fortuna, nulla spe rerum gerendarum, opifices denique delectantur historia? Duo enim genera quae erant, fecit tria. Quae contraria sunt his, malane? Sed hoc sane concedamus. Re mihi non aeque satisfacit, et quidem locis pluribus. Quantam rem agas, ut Circeis qui habitet totum hunc mundum suum municipium esse existimet? Nulla erit controversia.

+ + + +
+ + + +
+

+ Quid enim necesse est, tamquam meretricem in matronarum coetum, sic voluptatem in virtutum concilium adducere? +

+
+ + + +
Atqui reperies, inquit, in hoc quidem pertinacem;
+ + + +

Immo istud quidem, inquam, quo loco quidque, nisi iniquum postulo, arbitratu meo. Nunc haec primum fortasse audientis servire debemus. Tamen a proposito, inquam, aberramus. Quae est igitur causa istarum angustiarum? Pollicetur certe. Nihil enim hoc differt. Iam enim adesse poterit. Nobis aliter videtur, recte secusne, postea;

+ + + +

Efficiens dici potest.

+ + + +

Prodest, inquit, mihi eo esse animo. Est enim effectrix multarum et magnarum voluptatum. Quo tandem modo? Sed tu istuc dixti bene Latine, parum plane. Quo invento omnis ab eo quasi capite de summo bono et malo disputatio ducitur. Quantum Aristoxeni ingenium consumptum videmus in musicis? Tecum optime, deinde etiam cum mediocri amico. Nos paucis ad haec additis finem faciamus aliquando;

+ + + +

Non enim ipsa genuit hominem, sed accepit a natura inchoatum.

+ + + +

Memini me adesse P. Qua igitur re ab deo vincitur, si aeternitate non vincitur? Atque his de rebus et splendida est eorum et illustris oratio. Quod non faceret, si in voluptate summum bonum poneret. Cur post Tarentum ad Archytam?

+ + + +

Quis negat? Quod autem satis est, eo quicquid accessit, nimium est; Est enim effectrix multarum et magnarum voluptatum. Sin ea non neglegemus neque tamen ad finem summi boni referemus, non multum ab Erilli levitate aberrabimus. An vero, inquit, quisquam potest probare, quod perceptfum, quod. At coluit ipse amicitias. Tecum optime, deinde etiam cum mediocri amico. Quid de Pythagora? Itaque hic ipse iam pridem est reiectus; Quae cum praeponunt, ut sit aliqua rerum selectio, naturam videntur sequi;

+ + + +

Quia dolori non voluptas contraria est, sed doloris privatio.

+ + + +

Inde sermone vario sex illa a Dipylo stadia confecimus. Cuius quidem, quoniam Stoicus fuit, sententia condemnata mihi videtur esse inanitas ista verborum. Bonum valitudo: miser morbus. Itaque rursus eadem ratione, qua sum paulo ante usus, haerebitis. Zenonis est, inquam, hoc Stoici. Ab hoc autem quaedam non melius quam veteres, quaedam omnino relicta. Si est nihil nisi corpus, summa erunt illa: valitudo, vacuitas doloris, pulchritudo, cetera. Ita relinquet duas, de quibus etiam atque etiam consideret.

+ + + +
Mihi enim erit isdem istis fortasse iam utendum.
+ + + +

An potest, inquit ille, quicquam esse suavius quam nihil dolere? Venit enim mihi Platonis in mentem, quem accepimus primum hic disputare solitum; Quae quidem sapientes sequuntur duce natura tamquam videntes; Sed tamen intellego quid velit. Sed ad rem redeamus; Ea possunt paria non esse. Mene ergo et Triarium dignos existimas, apud quos turpiter loquare? Summus dolor plures dies manere non potest?

+ + + +

Nihil opus est exemplis hoc facere longius. Cur tantas regiones barbarorum pedibus obiit, tot maria transmisit? Itaque in rebus minime obscuris non multus est apud eos disserendi labor. Sed in rebus apertissimis nimium longi sumus. Nam, ut paulo ante docui, augendae voluptatis finis est doloris omnis amotio. Iam id ipsum absurdum, maximum malum neglegi. Sed haec omittamus; Quo modo autem philosophus loquitur? Te ipsum, dignissimum maioribus tuis, voluptasne induxit, ut adolescentulus eriperes P. Terram, mihi crede, ea lanx et maria deprimet.

+ + + +
+

+ Nam et a te perfici istam disputationem volo, nec tua mihi oratio longa videri potest. +

+
+ + + +
    +
  • Alterum significari idem, ut si diceretur, officia media omnia aut pleraque servantem vivere.
  • + + + +
  • Quid enim tanto opus est instrumento in optimis artibus comparandis?
  • + + + +
  • At cum de plurimis eadem dicit, tum certe de maximis.
  • +
+ + + +

Scio enim esse quosdam, qui quavis lingua philosophari possint; Theophrastus mediocriterne delectat, cum tractat locos ab Aristotele ante tractatos? Ille incendat? Ex eorum enim scriptis et institutis cum omnis doctrina liberalis, omnis historia. Quae duo sunt, unum facit. Deinde prima illa, quae in congressu solemus: Quid tu, inquit, huc?

+ + + +
+

+ Praeclare, inquit, facis, cum et eorum memoriam tenes, quorum uterque tibi testamento liberos suos commendavit, et puerum diligis. +

+
+ + + +

Sed nimis multa. Tu enim ista lenius, hic Stoicorum more nos vexat. Primum in nostrane potestate est, quid meminerimus? Quae cum ita sint, effectum est nihil esse malum, quod turpe non sit. Sed quid attinet de rebus tam apertis plura requirere? Cur igitur, inquam, res tam dissimiles eodem nomine appellas?

+ + + +
Magna laus.
+ + + +

Dici enim nihil potest verius. Satisne ergo pudori consulat, si quis sine teste libidini pareat? Scio enim esse quosdam, qui quavis lingua philosophari possint; Quod dicit Epicurus etiam de voluptate, quae minime sint voluptates, eas obscurari saepe et obrui. Quaesita enim virtus est, non quae relinqueret naturam, sed quae tueretur. Quamquam te quidem video minime esse deterritum. Quae adhuc, Cato, a te dicta sunt, eadem, inquam, dicere posses, si sequerere Pyrrhonem aut Aristonem. Mihi, inquam, qui te id ipsum rogavi?

+ + + +

Quis est, qui non oderit libidinosam, protervam adolescentiam? Quae animi affectio suum cuique tribuens atque hanc, quam dico. Faceres tu quidem, Torquate, haec omnia; An tu me de L. Quamquam te quidem video minime esse deterritum. Me igitur ipsum ames oportet, non mea, si veri amici futuri sumus. Ita ne hoc quidem modo paria peccata sunt. Quod quidem iam fit etiam in Academia.

+ + + +

An dolor longissimus quisque miserrimus, voluptatem non optabiliorem diuturnitas facit?

+ + + +

Miserum hominem! Si dolor summum malum est, dici aliter non potest. Aut, Pylades cum sis, dices te esse Orestem, ut moriare pro amico? Sed quanta sit alias, nunc tantum possitne esse tanta. Ac tamen hic mallet non dolere. Atque haec coniunctio confusioque virtutum tamen a philosophis ratione quadam distinguitur.

+ + + +
+

+ Completur enim et ex eo genere vitae, quod virtute fruitur, et ex iis rebus, quae sunt secundum naturam neque sunt in nostra potestate. +

+
+ + + +

Hoc est dicere: Non reprehenderem asotos, si non essent asoti.

+ + + +

At enim hic etiam dolore. Hanc ergo intuens debet institutum illud quasi signum absolvere. Certe, nisi voluptatem tanti aestimaretis. At quanta conantur! Mundum hunc omnem oppidum esse nostrum! Incendi igitur eos, qui audiunt, vides. Et non ex maxima parte de tota iudicabis? Sed ego in hoc resisto; Roges enim Aristonem, bonane ei videantur haec: vacuitas doloris, divitiae, valitudo;

+ + + +

Stulti autem malorum memoria torquentur, sapientes bona praeterita grata recordatione renovata delectant. Sunt autem, qui dicant foedus esse quoddam sapientium, ut ne minus amicos quam se ipsos diligant. Nescio quo modo praetervolavit oratio. Memini vero, inquam; Quae hic rei publicae vulnera inponebat, eadem ille sanabat. Omnis enim est natura diligens sui.

+ + + +
    +
  • Etsi qui potest intellegi aut cogitari esse aliquod animal, quod se oderit?
  • + + + +
  • Summum ením bonum exposuit vacuitatem doloris;
  • + + + +
  • Satisne ergo pudori consulat, si quis sine teste libidini pareat?
  • + + + +
  • Quamquam haec quidem praeposita recte et reiecta dicere licebit.
  • +
+ + + +

Quem si tenueris, non modo meum Ciceronem, sed etiam me ipsum abducas licebit. Quem ad modum quis ambulet, sedeat, qui ductus oris, qui vultus in quoque sit? Sint ista Graecorum; At hoc in eo M. Qui est in parvis malis.

+ + + +

Qui ita affectus, beatum esse numquam probabis;

+ + + +

Multoque hoc melius nos veriusque quam Stoici. Eaedem res maneant alio modo. Atque haec ita iustitiae propria sunt, ut sint virtutum reliquarum communia. Quarum ambarum rerum cum medicinam pollicetur, luxuriae licentiam pollicetur. Hic ego: Pomponius quidem, inquam, noster iocari videtur, et fortasse suo iure. Hinc ceteri particulas arripere conati suam quisque videro voluit afferre sententiam. Cum autem venissemus in Academiae non sine causa nobilitata spatia, solitudo erat ea, quam volueramus. Neque enim civitas in seditione beata esse potest nec in discordia dominorum domus; Sed erat aequius Triarium aliquid de dissensione nostra iudicare. Morbo gravissimo affectus, exul, orbus, egens, torqueatur eculeo: quem hunc appellas, Zeno? Ea possunt paria non esse. Itaque sensibus rationem adiunxit et ratione effecta sensus non reliquit.

+ + + +
+

+ Si est nihil nisi corpus, summa erunt illa: valitudo, vacuitas doloris, pulchritudo, cetera. +

+
+ + + +
+

+ Utrum igitur percurri omnem Epicuri disciplinam placet an de una voluptate quaeri, de qua omne certamen est? +

+
+ + + +
+

+ Atqui reperiemus asotos primum ita non religiosos, ut edint de patella, deinde ita mortem non timentes, ut illud in ore habeant ex Hymnide: Mihi sex menses satis sunt vitae, septimum Orco spondeo. +

+
+ + + +

Quorum altera prosunt, nocent altera. Virtutis, magnitudinis animi, patientiae, fortitudinis fomentis dolor mitigari solet. Cur deinde Metrodori liberos commendas? Quam ob rem tandem, inquit, non satisfacit? Pauca mutat vel plura sane; Et quidem Arcesilas tuus, etsi fuit in disserendo pertinacior, tamen noster fuit; Velut ego nunc moveor. Ergo ita: non posse honeste vivi, nisi honeste vivatur? Multoque hoc melius nos veriusque quam Stoici. Mihi, inquam, qui te id ipsum rogavi?

+ + + +

Nam quid possumus facere melius? Qua tu etiam inprudens utebare non numquam. Sit sane ista voluptas. Non est enim vitium in oratione solum, sed etiam in moribus. Nec mihi illud dixeris: Haec enim ipsa mihi sunt voluptati, et erant illa Torquatis. Non dolere, inquam, istud quam vim habeat postea videro; Tu quidem reddes; Nonne videmus quanta perturbatio rerum omnium consequatur, quanta confusio? Falli igitur possumus. Non ego tecum iam ita iocabor, ut isdem his de rebus, cum L.

+ + + +

Tibi hoc incredibile, quod beatissimum. Quid censes in Latino fore? Aut unde est hoc contritum vetustate proverbium: quicum in tenebris? Aliter enim explicari, quod quaeritur, non potest. Uterque enim summo bono fruitur, id est voluptate. Dolere malum est: in crucem qui agitur, beatus esse non potest.

+ + + +

Miserum hominem! Si dolor summum malum est, dici aliter non potest. Quis est, qui non oderit libidinosam, protervam adolescentiam? Verum tamen cum de rebus grandioribus dicas, ipsae res verba rapiunt; Sit enim idem caecus, debilis.

+ + + +
+

+ Quare istam quoque aggredere tractatam praesertim et ab aliis et a te ipso saepe, ut tibi deesse non possit oratio. +

+
+ + + +

Sin autem est in ea, quod quidam volunt, nihil impedit hanc nostram comprehensionem summi boni. Nec vero pietas adversus deos nec quanta iis gratia debeatur sine explicatione naturae intellegi potest.

+ + + +

Istam voluptatem perpetuam quis potest praestare sapienti? Et ille ridens: Video, inquit, quid agas; Primum divisit ineleganter; Traditur, inquit, ab Epicuro ratio neglegendi doloris. Aliter homines, aliter philosophos loqui putas oportere? In schola desinis. Aliter enim explicari, quod quaeritur, non potest. Ergo opifex plus sibi proponet ad formarum quam civis excellens ad factorum pulchritudinem? Proclivi currit oratio. Quod autem ratione actum est, id officium appellamus. Varietates autem iniurasque fortunae facile veteres philosophorum praeceptis instituta vita superabat.

+ + + +

Cur post Tarentum ad Archytam? Quae quidem sapientes sequuntur duce natura tamquam videntes; Gloriosa ostentatio in constituendo summo bono. Tecum optime, deinde etiam cum mediocri amico. Philosophi autem in suis lectulis plerumque moriuntur. Haec igitur Epicuri non probo, inquam. Ad quorum et cognitionem et usum iam corroborati natura ipsa praeeunte deducimur. Quid ait Aristoteles reliquique Platonis alumni? Audax negotium, dicerem impudens, nisi hoc institutum postea translatum ad philosophos nostros esset. At miser, si in flagitiosa et vitiosa vita afflueret voluptatibus.

+ + + +

Quodsi ipsam honestatem undique pertectam atque absolutam. Eam tum adesse, cum dolor omnis absit; Polycratem Samium felicem appellabant.

+ + + +
    +
  • Ubi ut eam caperet aut quando?
  • + + + +
  • Quod autem ratione actum est, id officium appellamus.
  • + + + +
  • Tu enim ista lenius, hic Stoicorum more nos vexat.
  • + + + +
  • Qui igitur convenit ab alia voluptate dicere naturam proficisci, in alia summum bonum ponere?
  • +
+ + + +

Si qua in iis corrigere voluit, deteriora fecit. Quae cum dixisset paulumque institisset, Quid est? Primum cur ista res digna odio est, nisi quod est turpis? Beatus autem esse in maximarum rerum timore nemo potest.

+ + + +
+

+ Nos cum te, M. +

+
+ + + +

Me igitur ipsum ames oportet, non mea, si veri amici futuri sumus. Cur deinde Metrodori liberos commendas? Que Manilium, ab iisque M. Bonum incolumis acies: misera caecitas. Quodsi ipsam honestatem undique pertectam atque absolutam. Collatio igitur ista te nihil iuvat.

+ + + +

Beatus autem esse in maximarum rerum timore nemo potest.

+ + + +

Qui autem de summo bono dissentit de tota philosophiae ratione dissentit. Cetera illa adhibebat, quibus demptis negat se Epicurus intellegere quid sit bonum. Nam si amitti vita beata potest, beata esse non potest. Illa tamen simplicia, vestra versuta. Neminem videbis ita laudatum, ut artifex callidus comparandarum voluptatum diceretur. Sed in rebus apertissimis nimium longi sumus. Peccata paria. An haec ab eo non dicuntur?

+ + + +

Tu autem negas fortem esse quemquam posse, qui dolorem malum putet. Nunc vides, quid faciat. Mihi enim satis est, ipsis non satis. Atque his de rebus et splendida est eorum et illustris oratio. Vestri haec verecundius, illi fortasse constantius. Quid enim necesse est, tamquam meretricem in matronarum coetum, sic voluptatem in virtutum concilium adducere? Nihil opus est exemplis hoc facere longius. Sed ad rem redeamus; Sed ea mala virtuti magnitudine obruebantur. Quae cum dixisset, finem ille. Ut aliquid scire se gaudeant?

+ + + +

Hoc simile tandem est?

+ + + +

Idem iste, inquam, de voluptate quid sentit? Omnia contraria, quos etiam insanos esse vultis. Scio enim esse quosdam, qui quavis lingua philosophari possint; Multoque hoc melius nos veriusque quam Stoici. Utrum igitur tibi litteram videor an totas paginas commovere? Dici enim nihil potest verius. Ex ea difficultate illae fallaciloquae, ut ait Accius, malitiae natae sunt. Rhetorice igitur, inquam, nos mavis quam dialectice disputare?

+ + + +

An potest, inquit ille, quicquam esse suavius quam nihil dolere? Ea possunt paria non esse. Nos quidem Virtutes sic natae sumus, ut tibi serviremus, aliud negotii nihil habemus. Cur ipse Pythagoras et Aegyptum lustravit et Persarum magos adiit? Cum id fugiunt, re eadem defendunt, quae Peripatetici, verba. Nam illud vehementer repugnat, eundem beatum esse et multis malis oppressum.

+ + + +

Hoc dixerit potius Ennius: Nimium boni est, cui nihil est mali.

+ + + +

Quorum altera prosunt, nocent altera. Dat enim intervalla et relaxat. Nam quibus rebus efficiuntur voluptates, eae non sunt in potestate sapientis. Hanc quoque iucunditatem, si vis, transfer in animum; Quare attende, quaeso. Eam tum adesse, cum dolor omnis absit;

+ + + +

Fortasse id optimum, sed ubi illud: Plus semper voluptatis?

+ + + +

Minime vero, inquit ille, consentit. Confecta res esset. Idne consensisse de Calatino plurimas gentis arbitramur, primarium populi fuisse, quod praestantissimus fuisset in conficiendis voluptatibus? Post enim Chrysippum eum non sane est disputatum. Pudebit te, inquam, illius tabulae, quam Cleanthes sane commode verbis depingere solebat. Sed tu istuc dixti bene Latine, parum plane. Falli igitur possumus.

+ + + +

Semper enim ex eo, quod maximas partes continet latissimeque funditur, tota res appellatur. Sed haec nihil sane ad rem; Confecta res esset. Quare conare, quaeso. Quae autem natura suae primae institutionis oblita est? Cur tantas regiones barbarorum pedibus obiit, tot maria transmisit?

+ + + +

In quo etsi est magnus, tamen nova pleraque et perpauca de moribus. Te ipsum, dignissimum maioribus tuis, voluptasne induxit, ut adolescentulus eriperes P. Hoc est dicere: Non reprehenderem asotos, si non essent asoti. An me, inquis, tam amentem putas, ut apud imperitos isto modo loquar?

+ + + +

Cuius quidem, quoniam Stoicus fuit, sententia condemnata mihi videtur esse inanitas ista verborum. Sed ego in hoc resisto; At, si voluptas esset bonum, desideraret.

+ + + +

Ab hoc autem quaedam non melius quam veteres, quaedam omnino relicta. Hoc loco tenere se Triarius non potuit. Cum id quoque, ut cupiebat, audivisset, evelli iussit eam, qua erat transfixus, hastam. Beatus sibi videtur esse moriens. Ergo id est convenienter naturae vivere, a natura discedere. Primum in nostrane potestate est, quid meminerimus?

+ + + +
+ + + +
+

+ Ex quo intellegi debet homini id esse in bonis ultimum, secundum naturam vivere, quod ita interpretemur: vivere ex hominis natura undique perfecta et nihil requirente. +

+
+ + + +

Itaque rursus eadem ratione, qua sum paulo ante usus, haerebitis. Hoc loco tenere se Triarius non potuit. Quae similitudo in genere etiam humano apparet. Quid ergo hoc loco intellegit honestum? Satis est ad hoc responsum. Quod ea non occurrentia fingunt, vincunt Aristonem; Eam si varietatem diceres, intellegerem, ut etiam non dicente te intellego; Quae cum essent dicta, discessimus. Neminem videbis ita laudatum, ut artifex callidus comparandarum voluptatum diceretur. Est enim effectrix multarum et magnarum voluptatum.

+ + + +
Themistocles quidem, cum ei Simonides an quis alius artem memoriae polliceretur, Oblivionis, inquit, mallem.
+ + + +

Tria genera bonorum; Neminem videbis ita laudatum, ut artifex callidus comparandarum voluptatum diceretur. Nam de isto magna dissensio est. Dicimus aliquem hilare vivere;

+ + + +
    +
  • Universa enim illorum ratione cum tota vestra confligendum puto.
  • + + + +
  • Iam enim adesse poterit.
  • + + + +
  • Haec et tu ita posuisti, et verba vestra sunt.
  • + + + +
  • Utrum igitur tibi litteram videor an totas paginas commovere?
  • + + + +
  • Haec bene dicuntur, nec ego repugno, sed inter sese ipsa pugnant.
  • + + + +
  • Quod si ita sit, cur opera philosophiae sit danda nescio.
  • +
+ + + +

Nihil opus est exemplis hoc facere longius. Aliena dixit in physicis nec ea ipsa, quae tibi probarentur; Quid, quod res alia tota est? Videmusne ut pueri ne verberibus quidem a contemplandis rebus perquirendisque deterreantur? Nullus est igitur cuiusquam dies natalis. Aut haec tibi, Torquate, sunt vituperanda aut patrocinium voluptatis repudiandum. Sed ille, ut dixi, vitiose.

+ + + +

Quonam, inquit, modo? Eodem modo is enim tibi nemo dabit, quod, expetendum sit, id esse laudabile. Et ego: Piso, inquam, si est quisquam, qui acute in causis videre soleat quae res agatur. Immo videri fortasse. Etenim semper illud extra est, quod arte comprehenditur.

+ + + +

De quibus cupio scire quid sentias. At ego quem huic anteponam non audeo dicere; Quid, cum fictas fabulas, e quibus utilitas nulla elici potest, cum voluptate legimus? Nam Metrodorum non puto ipsum professum, sed, cum appellaretur ab Epicuro, repudiare tantum beneficium noluisse; Falli igitur possumus. Magni enim aestimabat pecuniam non modo non contra leges, sed etiam legibus partam. Idemne, quod iucunde? Si alia sentit, inquam, alia loquitur, numquam intellegam quid sentiat; Et quod est munus, quod opus sapientiae? At, si voluptas esset bonum, desideraret.

+ + + +

Etsi qui potest intellegi aut cogitari esse aliquod animal, quod se oderit? Huic mori optimum esse propter desperationem sapientiae, illi propter spem vivere. Non pugnem cum homine, cur tantum habeat in natura boni; Sed ea mala virtuti magnitudine obruebantur. Ille incendat? Suam denique cuique naturam esse ad vivendum ducem. Nam de isto magna dissensio est.

+ + + +

Verum hoc idem saepe faciamus. Qui-vere falsone, quaerere mittimus-dicitur oculis se privasse; Nam quibus rebus efficiuntur voluptates, eae non sunt in potestate sapientis. Sed fortuna fortis; Nam diligi et carum esse iucundum est propterea, quia tutiorem vitam et voluptatem pleniorem efficit. Hoc ipsum elegantius poni meliusque potuit.

+ + + +

Quid igitur dubitamus in tota eius natura quaerere quid sit effectum?

+ + + +

Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Beatus autem esse in maximarum rerum timore nemo potest. Octavio fuit, cum illam severitatem in eo filio adhibuit, quem in adoptionem D. Minime vero istorum quidem, inquit.

+ + + +

Odium autem et invidiam facile vitabis. Nec vero alia sunt quaerenda contra Carneadeam illam sententiam. Ego vero isti, inquam, permitto. Comprehensum, quod cognitum non habet? Quod non faceret, si in voluptate summum bonum poneret. Non enim, si omnia non sequebatur, idcirco non erat ortus illinc. Rapior illuc, revocat autem Antiochus, nec est praeterea, quem audiamus. Aliter enim explicari, quod quaeritur, non potest. Itaque hic ipse iam pridem est reiectus;

+ + + +
    +
  • Quod idem cum vestri faciant, non satis magnam tribuunt inventoribus gratiam.
  • + + + +
  • Quae enim adhuc protulisti, popularia sunt, ego autem a te elegantiora desidero.
  • + + + +
  • Si longus, levis dictata sunt.
  • + + + +
  • Sed virtutem ipsam inchoavit, nihil amplius.
  • + + + +
  • Suo genere perveniant ad extremum;
  • +
+ + + +

Omnia contraria, quos etiam insanos esse vultis. Si de re disceptari oportet, nulla mihi tecum, Cato, potest esse dissensio. Murenam te accusante defenderem. Quam tu ponis in verbis, ego positam in re putabam. Est enim tanti philosophi tamque nobilis audacter sua decreta defendere. Sit enim idem caecus, debilis. Eaedem enim utilitates poterunt eas labefactare atque pervertere. Quid enim de amicitia statueris utilitatis causa expetenda vides. Si id dicis, vicimus.

+ + + +
+

+ Quod dicit Epicurus etiam de voluptate, quae minime sint voluptates, eas obscurari saepe et obrui. +

+
+ + + +
+

+ Omnesque eae sunt genere quattuor, partibus plures, aegritudo, formido, libido, quamque Stoici communi nomine corporis et animi ¾donÆn appellant, ego malo laetitiam appellare, quasi gestientis animi elationem voluptariam. +

+
+ + + +
+

+ Nec enim figura corporis nec ratio excellens ingenii humani significat ad unam hanc rem natum hominem, ut frueretur voluptatibus. +

+
+ + + +

Quod si ita sit, cur opera philosophiae sit danda nescio. Scrupulum, inquam, abeunti; Superiores tres erant, quae esse possent, quarum est una sola defensa, eaque vehementer. Tria genera cupiditatum, naturales et necessariae, naturales et non necessariae, nec naturales nec necessariae. In qua quid est boni praeter summam voluptatem, et eam sempiternam? Mihi enim erit isdem istis fortasse iam utendum. Utilitatis causa amicitia est quaesita.

+ + + +

Videmus igitur ut conquiescere ne infantes quidem possint. Sic enim maiores nostri labores non fugiendos tristissimo tamen verbo aerumnas etiam in deo nominaverunt. Qui autem de summo bono dissentit de tota philosophiae ratione dissentit. Sed haec omittamus; Quae ista amicitia est?

+ + + +
+

+ Quod enim testimonium maius quaerimus, quae honesta et recta sint, ipsa esse optabilia per sese, cum videamus tanta officia morientis? +

+
+ + + +

Tamen a proposito, inquam, aberramus. Id mihi magnum videtur. Non risu potius quam oratione eiciendum? Idem iste, inquam, de voluptate quid sentit? Ego quoque, inquit, didicerim libentius si quid attuleris, quam te reprehenderim. At ille pellit, qui permulcet sensum voluptate.

+ + + +

Tamen a proposito, inquam, aberramus. Gerendus est mos, modo recte sentiat. Cum praesertim illa perdiscere ludus esset. Sed quanta sit alias, nunc tantum possitne esse tanta. Itaque hic ipse iam pridem est reiectus;

+ + + +

Nihil opus est exemplis hoc facere longius. Eam tum adesse, cum dolor omnis absit; Inscite autem medicinae et gubernationis ultimum cum ultimo sapientiae comparatur. Vide igitur ne non debeas verbis nostris uti, sententiis tuis. Ad eas enim res ab Epicuro praecepta dantur. Eodem modo is enim tibi nemo dabit, quod, expetendum sit, id esse laudabile.

+ + + +

Nam Pyrrho, Aristo, Erillus iam diu abiecti. Itaque ad tempus ad Pisonem omnes. Omnia contraria, quos etiam insanos esse vultis. At certe gravius. Atque haec ita iustitiae propria sunt, ut sint virtutum reliquarum communia. At, si voluptas esset bonum, desideraret.

+ + + +

Sed erat aequius Triarium aliquid de dissensione nostra iudicare.

+ + + +

Quasi ego id curem, quid ille aiat aut neget. Bestiarum vero nullum iudicium puto. Restatis igitur vos; Summum ením bonum exposuit vacuitatem doloris; Quare ad ea primum, si videtur; Erat enim Polemonis.

+ + + +
    +
  • Disserendi artem nullam habuit.
  • + + + +
  • Ita relinquet duas, de quibus etiam atque etiam consideret.
  • + + + +
  • Et certamen honestum et disputatio splendida! omnis est enim de virtutis dignitate contentio.
  • + + + +
  • Transfer idem ad modestiam vel temperantiam, quae est moderatio cupiditatum rationi oboediens.
  • + + + +
  • Sin dicit obscurari quaedam nec apparere, quia valde parva sint, nos quoque concedimus;
  • +
+ + + +
+

+ Nemo enim est, qui aliter dixerit quin omnium naturarum simile esset id, ad quod omnia referrentur, quod est ultimum rerum appetendarum. +

+
+ + + +

Qui enim existimabit posse se miserum esse beatus non erit.

+ + + +

Quod non faceret, si in voluptate summum bonum poneret. Qui enim voluptatem ipsam contemnunt, iis licet dicere se acupenserem maenae non anteponere. Inde sermone vario sex illa a Dipylo stadia confecimus. Mihi, inquam, qui te id ipsum rogavi? Nemo nostrum istius generis asotos iucunde putat vivere. An me, inquis, tam amentem putas, ut apud imperitos isto modo loquar?

+ + + +

Neque enim civitas in seditione beata esse potest nec in discordia dominorum domus; Eam stabilem appellas. Quacumque enim ingredimur, in aliqua historia vestigium ponimus. Quamquam id quidem licebit iis existimare, qui legerint. Sed ad haec, nisi molestum est, habeo quae velim. Num igitur eum postea censes anxio animo aut sollicito fuisse? Aut haec tibi, Torquate, sunt vituperanda aut patrocinium voluptatis repudiandum. Graece donan, Latine voluptatem vocant. Quamquam te quidem video minime esse deterritum. Reguli reiciendam;

+ + + +
    +
  • Non potes ergo ista tueri, Torquate, mihi crede, si te ipse et tuas cogitationes et studia perspexeris;
  • + + + +
  • Quod si ita sit, cur opera philosophiae sit danda nescio.
  • + + + +
  • Occultum facinus esse potuerit, gaudebit;
  • +
+ + + +

Hoc enim identidem dicitis, non intellegere nos quam dicatis voluptatem. Prodest, inquit, mihi eo esse animo. Non igitur de improbo, sed de callido improbo quaerimus, qualis Q. An vero, inquit, quisquam potest probare, quod perceptfum, quod. Intellegi quidem, ut propter aliam quampiam rem, verbi gratia propter voluptatem, nos amemus; Quamquam id quidem, infinitum est in hac urbe; Quae hic rei publicae vulnera inponebat, eadem ille sanabat. Quamquam ab iis philosophiam et omnes ingenuas disciplinas habemus; Quid enim me prohiberet Epicureum esse, si probarem, quae ille diceret? Quae ista amicitia est? Portenta haec esse dicit, neque ea ratione ullo modo posse vivi; Cur tantas regiones barbarorum pedibus obiit, tot maria transmisit?

+ + + +

Suam denique cuique naturam esse ad vivendum ducem. Summae mihi videtur inscitiae.

+ + + +

Non quam nostram quidem, inquit Pomponius iocans;

+ + + +

Quod autem ratione actum est, id officium appellamus. Quorum sine causa fieri nihil putandum est. Idemque diviserunt naturam hominis in animum et corpus. Facile est hoc cernere in primis puerorum aetatulis.

+ + + +
Ad eas enim res ab Epicuro praecepta dantur.
+ + + +

Ergo et avarus erit, sed finite, et adulter, verum habebit modum, et luxuriosus eodem modo. Sed tempus est, si videtur, et recta quidem ad me. Si quidem, inquit, tollerem, sed relinquo. Paulum, cum regem Persem captum adduceret, eodem flumine invectio? Sin aliud quid voles, postea. Tum Triarius: Posthac quidem, inquit, audacius.

+ + + +
    +
  • Utilitatis causa amicitia est quaesita.
  • + + + +
  • Cuius similitudine perspecta in formarum specie ac dignitate transitum est ad honestatem dictorum atque factorum.
  • + + + +
  • Mihi quidem Homerus huius modi quiddam vidisse videatur in iis, quae de Sirenum cantibus finxerit.
  • +
+ + + +

Quamquam te quidem video minime esse deterritum. Tum mihi Piso: Quid ergo? Que Manilium, ab iisque M. Peccata paria. Atque haec ita iustitiae propria sunt, ut sint virtutum reliquarum communia. Beatum, inquit. Ne vitationem quidem doloris ipsam per se quisquam in rebus expetendis putavit, nisi etiam evitare posset. Et quidem saepe quaerimus verbum Latinum par Graeco et quod idem valeat;

+ + + +

Duae sunt enim res quoque, ne tu verba solum putes. Quo tandem modo? Itaque contra est, ac dicitis; Dicet pro me ipsa virtus nec dubitabit isti vestro beato M. Quam ob rem tandem, inquit, non satisfacit? Hunc vos beatum; Nunc omni virtuti vitium contrario nomine opponitur.

+ + + +

Quorum sine causa fieri nihil putandum est. Ego vero isti, inquam, permitto. Illa videamus, quae a te de amicitia dicta sunt. Illa argumenta propria videamus, cur omnia sint paria peccata. Aufidio, praetorio, erudito homine, oculis capto, saepe audiebam, cum se lucis magis quam utilitatis desiderio moveri diceret. Itaque his sapiens semper vacabit. Num quid tale Democritus?

+ + + +
+

+ Quam ob rem utique idem faciunt, ut si laevam partem neglegerent, dexteram tuerentur, aut ipsius animi, ut fecit Erillus, cognitionem amplexarentur, actionem relinquerent. +

+
+ + + +

Isto modo, ne si avia quidem eius nata non esset. An, partus ancillae sitne in fructu habendus, disseretur inter principes civitatis, P. Quod idem cum vestri faciant, non satis magnam tribuunt inventoribus gratiam. Videmusne ut pueri ne verberibus quidem a contemplandis rebus perquirendisque deterreantur? Graece donan, Latine voluptatem vocant. Expressa vero in iis aetatibus, quae iam confirmatae sunt.

+ + + +
    +
  • Sit hoc ultimum bonorum, quod nunc a me defenditur;
  • + + + +
  • Vos autem cum perspicuis dubia debeatis illustrare, dubiis perspicua conamini tollere.
  • + + + +
  • Ipse Epicurus fortasse redderet, ut Sextus Peducaeus, Sex.
  • + + + +
  • Non est igitur summum malum dolor.
  • +
+ + + +

Primum in nostrane potestate est, quid meminerimus? Isto modo, ne si avia quidem eius nata non esset. Illa argumenta propria videamus, cur omnia sint paria peccata. Quae si potest singula consolando levare, universa quo modo sustinebit? Bona autem corporis huic sunt, quod posterius posui, similiora. Hoc loco discipulos quaerere videtur, ut, qui asoti esse velint, philosophi ante fiant. Sapientem locupletat ipsa natura, cuius divitias Epicurus parabiles esse docuit.

+ + + +

Quis Aristidem non mortuum diligit?

+ + + +

Non igitur de improbo, sed de callido improbo quaerimus, qualis Q. Ratio quidem vestra sic cogit. Venit ad extremum; Conferam avum tuum Drusum cum C.

+ + + +

Sed quot homines, tot sententiae; Sed quod proximum fuit non vidit. Quid de Platone aut de Democrito loquar? Idem adhuc; Quonam modo? Materiam vero rerum et copiam apud hos exilem, apud illos uberrimam reperiemus. At eum nihili facit; Nondum autem explanatum satis, erat, quid maxime natura vellet. Tibi hoc incredibile, quod beatissimum. Sed haec nihil sane ad rem;

+ + + +

Videmusne ut pueri ne verberibus quidem a contemplandis rebus perquirendisque deterreantur? Quid ait Aristoteles reliquique Platonis alumni? Qui autem diffidet perpetuitati bonorum suorum, timeat necesse est, ne aliquando amissis illis sit miser.

+ + + +
+

+ Earum etiam rerum, quas terra gignit, educatio quaedam et perfectio est non dissimilis animantium. +

+
+ + + +

Sed nimis multa. At enim hic etiam dolore. Nihilo magis. An me, inquam, nisi te audire vellem, censes haec dicturum fuisse? Ab his oratores, ab his imperatores ac rerum publicarum principes extiterunt. Ita similis erit ei finis boni, atque antea fuerat, neque idem tamen; Sedulo, inquam, faciam. Is ita vivebat, ut nulla tam exquisita posset inveniri voluptas, qua non abundaret.

+ + + +

Ut id aliis narrare gestiant? Quam multa vitiosa! summum enim bonum et malum vagiens puer utra voluptate diiudicabit, stante an movente? Videsne quam sit magna dissensio? In eo enim positum est id, quod dicimus esse expetendum. Eadem nunc mea adversum te oratio est.

+ + + +

Ad eos igitur converte te, quaeso. Ut aliquid scire se gaudeant? Itaque e contrario moderati aequabilesque habitus, affectiones ususque corporis apti esse ad naturam videntur. Universa enim illorum ratione cum tota vestra confligendum puto. Videamus animi partes, quarum est conspectus illustrior;

+ + + +

Quae quo sunt excelsiores, eo dant clariora indicia naturae.

+ + + +

Igitur neque stultorum quisquam beatus neque sapientium non beatus. Cur igitur, inquam, res tam dissimiles eodem nomine appellas? Immo alio genere; Pauca mutat vel plura sane; Equidem, sed audistine modo de Carneade? Bestiarum vero nullum iudicium puto. Quae cum dixisset paulumque institisset, Quid est? Haec quo modo conveniant, non sane intellego. Iam in altera philosophiae parte. Nec vero sum nescius esse utilitatem in historia, non modo voluptatem.

+ + + +

Ut enim consuetudo loquitur, id solum dicitur honestum, quod est populari fama gloriosum. Simus igitur contenti his.

+ + + +
+

+ Utrum enim sit voluptas in iis rebus, quas primas secundum naturam esse diximus, necne sit ad id, quod agimus, nihil interest. +

+
+ + + +
+

+ Nam si dicent ab illis has res esse tractatas, ne ipsos quidem Graecos est cur tam multos legant, quam legendi sunt. +

+
+ + + +

Quae cum essent dicta, discessimus.

+ + + +

Experiamur igitur, inquit, etsi habet haec Stoicorum ratio difficilius quiddam et obscurius. Non est ista, inquam, Piso, magna dissensio. Varietates autem iniurasque fortunae facile veteres philosophorum praeceptis instituta vita superabat. Ergo opifex plus sibi proponet ad formarum quam civis excellens ad factorum pulchritudinem? Tu enim ista lenius, hic Stoicorum more nos vexat. Res enim concurrent contrariae. Scientiam pollicentur, quam non erat mirum sapientiae cupido patria esse cariorem. Eiuro, inquit adridens, iniquum, hac quidem de re;

+ + + +

Itaque et manendi in vita et migrandi ratio omnis iis rebus, quas supra dixi, metienda. Non quaeritur autem quid naturae tuae consentaneum sit, sed quid disciplinae. Traditur, inquit, ab Epicuro ratio neglegendi doloris. Aeque enim contingit omnibus fidibus, ut incontentae sint. Iis igitur est difficilius satis facere, qui se Latina scripta dicunt contemnere. Sedulo, inquam, faciam. Eorum enim omnium multa praetermittentium, dum eligant aliquid, quod sequantur, quasi curta sententia;

+ + + +

Honesta oratio, Socratica, Platonis etiam.

+ + + +
+ + + +

Tanta vis admonitionis inest in locis; Miserum hominem! Si dolor summum malum est, dici aliter non potest. Atqui reperies, inquit, in hoc quidem pertinacem; Ergo hoc quidem apparet, nos ad agendum esse natos. Illis videtur, qui illud non dubitant bonum dicere -; Itaque contra est, ac dicitis; Nihil est enim, de quo aliter tu sentias atque ego, modo commutatis verbis ipsas res conferamus. Occultum facinus esse potuerit, gaudebit; Quod cum accidisset ut alter alterum necopinato videremus, surrexit statim. Quare attende, quaeso.

+ + + +
+

+ Sed quid minus probandum quam esse aliquem beatum nec satis beatum? +

+
+ + + +

At ille non pertimuit saneque fidenter: Istis quidem ipsis verbis, inquit;

+ + + +

Suam denique cuique naturam esse ad vivendum ducem. At ille non pertimuit saneque fidenter: Istis quidem ipsis verbis, inquit; Quid adiuvas? Quamquam id quidem, infinitum est in hac urbe; Quod non faceret, si in voluptate summum bonum poneret. Negare non possum. Heri, inquam, ludis commissis ex urbe profectus veni ad vesperum. Quid enim ab antiquis ex eo genere, quod ad disserendum valet, praetermissum est? Quid iudicant sensus? Itaque his sapiens semper vacabit.

+ + + +

Illa argumenta propria videamus, cur omnia sint paria peccata.

+ + + +

An potest cupiditas finiri? Sic igitur in homine perfectio ista in eo potissimum, quod est optimum, id est in virtute, laudatur. Scisse enim te quis coarguere possit? Fortasse id optimum, sed ubi illud: Plus semper voluptatis? Hanc se tuus Epicurus omnino ignorare dicit quam aut qualem esse velint qui honestate summum bonum metiantur. Non ego tecum iam ita iocabor, ut isdem his de rebus, cum L. Superiores tres erant, quae esse possent, quarum est una sola defensa, eaque vehementer. Erat enim Polemonis.

+ + + +

Compensabatur, inquit, cum summis doloribus laetitia. An tu me de L. Alterum significari idem, ut si diceretur, officia media omnia aut pleraque servantem vivere. Eorum enim est haec querela, qui sibi cari sunt seseque diligunt. Et si turpitudinem fugimus in statu et motu corporis, quid est cur pulchritudinem non sequamur? At iam decimum annum in spelunca iacet. Sed fac ista esse non inportuna; Nam de isto magna dissensio est. Illa tamen simplicia, vestra versuta. An tu me de L.

+ + + +

Quodcumque in mentem incideret, et quodcumque tamquam occurreret.

+ + + +

Cur igitur, inquam, res tam dissimiles eodem nomine appellas? Iam illud quale tandem est, bona praeterita non effluere sapienti, mala meminisse non oportere? Nam et complectitur verbis, quod vult, et dicit plane, quod intellegam; Quid ad utilitatem tantae pecuniae? Easdemne res? Superiores tres erant, quae esse possent, quarum est una sola defensa, eaque vehementer. Nonne igitur tibi videntur, inquit, mala? Sine ea igitur iucunde negat posse se vivere?

+ + + +
    +
  • Cuius quidem, quoniam Stoicus fuit, sententia condemnata mihi videtur esse inanitas ista verborum.
  • + + + +
  • Vulgo enim dicitur: Iucundi acti labores, nec male Euripidesconcludam, si potero, Latine;
  • + + + +
  • Ab his oratores, ab his imperatores ac rerum publicarum principes extiterunt.
  • + + + +
  • Sed tu, ut dignum est tua erga me et philosophiam voluntate ab adolescentulo suscepta, fac ut Metrodori tueare liberos.
  • +
+ + + +

Peccata paria. Quae si potest singula consolando levare, universa quo modo sustinebit? Illa argumenta propria videamus, cur omnia sint paria peccata. Hinc ceteri particulas arripere conati suam quisque videro voluit afferre sententiam. Portenta haec esse dicit, neque ea ratione ullo modo posse vivi; Etsi qui potest intellegi aut cogitari esse aliquod animal, quod se oderit? Multa sunt dicta ab antiquis de contemnendis ac despiciendis rebus humanis; Quod autem in homine praestantissimum atque optimum est, id deseruit.

+ + + +
+

+ Admirantes quaeramus ab utroque, quonam modo vitam agere possimus, si nihil interesse nostra putemus, valeamus aegrine simus, vacemus an cruciemur dolore, frigus, famem propulsare possimus necne possimus. +

+
+ + + +

Quae cum ita sint, effectum est nihil esse malum, quod turpe non sit. Non autem hoc: igitur ne illud quidem. Solum praeterea formosum, solum liberum, solum civem, stultost; Faceres tu quidem, Torquate, haec omnia; Iam id ipsum absurdum, maximum malum neglegi. Res tota, Torquate, non doctorum hominum, velle post mortem epulis celebrari memoriam sui nominis. Numquam facies. Illi enim inter se dissentiunt.

+ + + +

Non autem hoc: igitur ne illud quidem.

+ + + +

Si verbum sequimur, primum longius verbum praepositum quam bonum. Nam Pyrrho, Aristo, Erillus iam diu abiecti. Et quidem saepe quaerimus verbum Latinum par Graeco et quod idem valeat; Negat enim summo bono afferre incrementum diem. Placet igitur tibi, Cato, cum res sumpseris non concessas, ex illis efficere, quod velis? Quod quidem iam fit etiam in Academia.

+ + + +

Sin autem est in ea, quod quidam volunt, nihil impedit hanc nostram comprehensionem summi boni. Satis est ad hoc responsum. Quonam, inquit, modo? Quo tandem modo? Beatus sibi videtur esse moriens. Eam si varietatem diceres, intellegerem, ut etiam non dicente te intellego; Deinde dolorem quem maximum? Ex rebus enim timiditas, non ex vocabulis nascitur. Laelius clamores sofòw ille so lebat Edere compellans gumias ex ordine nostros. Laboro autem non sine causa;

+ + + +

Nam aliquando posse recte fieri dicunt nulla expectata nec quaesita voluptate.

+ + + +

Cenasti in vita numquam bene, cum omnia in ista Consumis squilla atque acupensere cum decimano. Cave putes quicquam esse verius. Tum Piso: Atqui, Cicero, inquit, ista studia, si ad imitandos summos viros spectant, ingeniosorum sunt; Tu enim ista lenius, hic Stoicorum more nos vexat. Minime vero istorum quidem, inquit.

+ + + +
    +
  • Quid turpius quam sapientis vitam ex insipientium sermone pendere?
  • + + + +
  • Quae quidem vel cum periculo est quaerenda vobis;
  • + + + +
  • Si longus, levis.
  • + + + +
  • Fortitudinis quaedam praecepta sunt ac paene leges, quae effeminari virum vetant in dolore.
  • +
+ + + +
+

+ Nec vero potest quisquam de bonis et malis vere iudicare nisi omni cognita ratione naturae et vitae etiam deorum, et utrum conveniat necne natura hominis cum universa. +

+
+ + + +
    +
  • Ergo et avarus erit, sed finite, et adulter, verum habebit modum, et luxuriosus eodem modo.
  • + + + +
  • Magno hic ingenio, sed res se tamen sic habet, ut nimis imperiosi philosophi sit vetare meminisse.
  • + + + +
  • Beatus autem esse in maximarum rerum timore nemo potest.
  • + + + +
  • Atqui iste locus est, Piso, tibi etiam atque etiam confirmandus, inquam;
  • + + + +
  • Primum in nostrane potestate est, quid meminerimus?
  • + + + +
  • Nec lapathi suavitatem acupenseri Galloni Laelius anteponebat, sed suavitatem ipsam neglegebat;
  • + + + +
  • Est enim effectrix multarum et magnarum voluptatum.
  • +
+ + + +

Ut in voluptate sit, qui epuletur, in dolore, qui torqueatur. Neque solum ea communia, verum etiam paria esse dixerunt. Scio enim esse quosdam, qui quavis lingua philosophari possint; Non potes, nisi retexueris illa. Si est nihil nisi corpus, summa erunt illa: valitudo, vacuitas doloris, pulchritudo, cetera. Et ais, si una littera commota sit, fore tota ut labet disciplina. Nam illud quidem adduci vix possum, ut ea, quae senserit ille, tibi non vera videantur. Hoc positum in Phaedro a Platone probavit Epicurus sensitque in omni disputatione id fieri oportere.

+ + + +
    +
  • Eiuro, inquit adridens, iniquum, hac quidem de re;
  • + + + +
  • At ille pellit, qui permulcet sensum voluptate.
  • + + + +
  • Primum in nostrane potestate est, quid meminerimus?
  • + + + +
  • Itaque vides, quo modo loquantur, nova verba fingunt, deserunt usitata.
  • +
+ + + +

Quid vero? Aut, Pylades cum sis, dices te esse Orestem, ut moriare pro amico? Si longus, levis; Aut unde est hoc contritum vetustate proverbium: quicum in tenebris? Immo alio genere; Sunt enim prima elementa naturae, quibus auctis vírtutis quasi germen efficitur. Potius inflammat, ut coercendi magis quam dedocendi esse videantur. Inde igitur, inquit, ordiendum est.

+ + + +

Nihilne te delectat umquam -video, quicum loquar-, te igitur, Torquate, ipsum per se nihil delectat?

+ + + +

Quo modo autem optimum, si bonum praeterea nullum est? Si quicquam extra virtutem habeatur in bonis. Conclusum est enim contra Cyrenaicos satis acute, nihil ad Epicurum. Itaque haec cum illis est dissensio, cum Peripateticis nulla sane. Illa sunt similia: hebes acies est cuipiam oculorum, corpore alius senescit; Primum cur ista res digna odio est, nisi quod est turpis?

+ + + +
    +
  • Plane idem, inquit, et maxima quidem, qua fieri nulla maior potest.
  • + + + +
  • An ea, quae per vinitorem antea consequebatur, per se ipsa curabit?
  • + + + +
  • Sit sane ista voluptas.
  • + + + +
  • -delector enim, quamquam te non possum, ut ais, corrumpere, delector, inquam, et familia vestra et nomine.
  • +
+ + + +

Intrandum est igitur in rerum naturam et penitus quid ea postulet pervidendum; Nemo igitur esse beatus potest. Quicquid enim a sapientia proficiscitur, id continuo debet expletum esse omnibus suis partibus; Sed tu istuc dixti bene Latine, parum plane. Summum a vobis bonum voluptas dicitur. Ergo et avarus erit, sed finite, et adulter, verum habebit modum, et luxuriosus eodem modo. Beatus sibi videtur esse moriens. Illa argumenta propria videamus, cur omnia sint paria peccata.

+ + + +
Egone quaeris, inquit, quid sentiam?
+ + + +

Estne, quaeso, inquam, sitienti in bibendo voluptas? Cum audissem Antiochum, Brute, ut solebam, cum M. Si mala non sunt, iacet omnis ratio Peripateticorum. Primum cur ista res digna odio est, nisi quod est turpis? Sed hoc sane concedamus. Deinde prima illa, quae in congressu solemus: Quid tu, inquit, huc?

+ + + +

Quid ergo hoc loco intellegit honestum? Inde sermone vario sex illa a Dipylo stadia confecimus. Minime vero, inquit ille, consentit. Occultum facinus esse potuerit, gaudebit; Dic in quovis conventu te omnia facere, ne doleas. Quid ergo hoc loco intellegit honestum? Pauca mutat vel plura sane; Illis videtur, qui illud non dubitant bonum dicere -; Tria genera bonorum; Quasi vero, inquit, perpetua oratio rhetorum solum, non etiam philosophorum sit. Qua igitur re ab deo vincitur, si aeternitate non vincitur?

+ + + +

Nullus est igitur cuiusquam dies natalis. Etenim nec iustitia nec amicitia esse omnino poterunt, nisi ipsae per se expetuntur. Sic vester sapiens magno aliquo emolumento commotus cicuta, si opus erit, dimicabit. Istam voluptatem perpetuam quis potest praestare sapienti? Et quod est munus, quod opus sapientiae? Quod totum contra est.

+ + + +

Quid ad utilitatem tantae pecuniae? Nos commodius agimus. Paulum, cum regem Persem captum adduceret, eodem flumine invectio? Illa videamus, quae a te de amicitia dicta sunt. Ad eas enim res ab Epicuro praecepta dantur. Huius ego nunc auctoritatem sequens idem faciam.

+ + + +

Habes, inquam, Cato, formam eorum, de quibus loquor, philosophorum. Ita similis erit ei finis boni, atque antea fuerat, neque idem tamen; Qui autem esse poteris, nisi te amor ipse ceperit? An vero displicuit ea, quae tributa est animi virtutibus tanta praestantia?

+ + + +

Videamus igitur sententias eorum, tum ad verba redeamus. Qui cum praetor quaestionem inter sicarios exercuisset, ita aperte cepit pecunias ob rem iudicandam, ut anno proximo P. Iam id ipsum absurdum, maximum malum neglegi. Mihi enim satis est, ipsis non satis. Quod autem principium officii quaerunt, melius quam Pyrrho; Quae cum dixisset paulumque institisset, Quid est? Omnia contraria, quos etiam insanos esse vultis. Hoc est dicere: Non reprehenderem asotos, si non essent asoti.

+ + + +

Addidisti ad extremum etiam indoctum fuisse. Quid, quod res alia tota est? In quibus doctissimi illi veteres inesse quiddam caeleste et divinum putaverunt. Bonum liberi: misera orbitas. Primum cur ista res digna odio est, nisi quod est turpis? Deprehensus omnem poenam contemnet. Luxuriam non reprehendit, modo sit vacua infinita cupiditate et timore. Illa argumenta propria videamus, cur omnia sint paria peccata. At quanta conantur! Mundum hunc omnem oppidum esse nostrum! Incendi igitur eos, qui audiunt, vides.

+ + + +

Quis negat? Aliter enim nosmet ipsos nosse non possumus. Non minor, inquit, voluptas percipitur ex vilissimis rebus quam ex pretiosissimis. Nec vero hoc oratione solum, sed multo magis vita et factis et moribus comprobavit. Ne amores quidem sanctos a sapiente alienos esse arbitrantur. Octavio fuit, cum illam severitatem in eo filio adhibuit, quem in adoptionem D.

+ + + +
Callipho ad virtutem nihil adiunxit nisi voluptatem, Diodorus vacuitatem doloris.
+ + + +

Huius ego nunc auctoritatem sequens idem faciam. Quis Aristidem non mortuum diligit? Quae qui non vident, nihil umquam magnum ac cognitione dignum amaverunt. Quam nemo umquam voluptatem appellavit, appellat; Quam si explicavisset, non tam haesitaret.

+ + + +

Hic Speusippus, hic Xenocrates, hic eius auditor Polemo, cuius illa ipsa sessio fuit, quam videmus. Plane idem, inquit, et maxima quidem, qua fieri nulla maior potest. Atque ab his initiis profecti omnium virtutum et originem et progressionem persecuti sunt. Omnis enim est natura diligens sui.

+ + + +
Nam his libris eum malo quam reliquo ornatu villae delectari.
+ + + +

Nulla profecto est, quin suam vim retineat a primo ad extremum. Esse enim quam vellet iniquus iustus poterat inpune. Non enim solum Torquatus dixit quid sentiret, sed etiam cur. Nullus est igitur cuiusquam dies natalis. Haec et tu ita posuisti, et verba vestra sunt. Satis est ad hoc responsum. Tum Triarius: Posthac quidem, inquit, audacius. Nulla profecto est, quin suam vim retineat a primo ad extremum. Qui est in parvis malis.

+ + + +

An potest cupiditas finiri? Ut in geometria, prima si dederis, danda sunt omnia. At ille non pertimuit saneque fidenter: Istis quidem ipsis verbis, inquit; Nulla profecto est, quin suam vim retineat a primo ad extremum. Cum autem negant ea quicquam ad beatam vitam pertinere, rursus naturam relinquunt. Huius ego nunc auctoritatem sequens idem faciam. Quod vestri non item. Quorum sine causa fieri nihil putandum est.

+ + + +
    +
  • Sed haec in pueris;
  • + + + +
  • Quid igitur dubitamus in tota eius natura quaerere quid sit effectum?
  • + + + +
  • Sed non alienum est, quo facilius vis verbi intellegatur, rationem huius verbi faciendi Zenonis exponere.
  • + + + +
  • Graecum enim hunc versum nostis omnes-: Suavis laborum est praeteritorum memoria.
  • +
+ + + +

Tamen a proposito, inquam, aberramus. Aliud igitur esse censet gaudere, aliud non dolere. Sic enim censent, oportunitatis esse beate vivere. Sumenda potius quam expetenda. Tum Torquatus: Prorsus, inquit, assentior; Haec et tu ita posuisti, et verba vestra sunt.

+ + + +

Vitiosum est enim in dividendo partem in genere numerare. Oratio me istius philosophi non offendit; Post enim Chrysippum eum non sane est disputatum. Aut unde est hoc contritum vetustate proverbium: quicum in tenebris? Vestri haec verecundius, illi fortasse constantius. Cur ipse Pythagoras et Aegyptum lustravit et Persarum magos adiit? Mihi vero, inquit, placet agi subtilius et, ut ipse dixisti, pressius. Quam ob rem tandem, inquit, non satisfacit?

+ + + +

Hoc enim identidem dicitis, non intellegere nos quam dicatis voluptatem. Aliud igitur esse censet gaudere, aliud non dolere. Cum audissem Antiochum, Brute, ut solebam, cum M. Quaesita enim virtus est, non quae relinqueret naturam, sed quae tueretur. Quibus ego vehementer assentior.

+ + + +
    +
  • Ego quoque, inquit, didicerim libentius si quid attuleris, quam te reprehenderim.
  • + + + +
  • Omnes enim iucundum motum, quo sensus hilaretur.
  • +
+ + + +
+

+ Posuisti etiam dicere alios foedus quoddam inter se facere sapientis, ut, quem ad modum sint in se ipsos animati, eodem modo sint erga amicos; +

+
+ + + +

Aliter enim explicari, quod quaeritur, non potest. Quod quidem iam fit etiam in Academia. Equidem, sed audistine modo de Carneade? Iam doloris medicamenta illa Epicurea tamquam de narthecio proment: Si gravis, brevis; Faceres tu quidem, Torquate, haec omnia; Quod cum dixissent, ille contra. Tecum optime, deinde etiam cum mediocri amico. Traditur, inquit, ab Epicuro ratio neglegendi doloris.

+ + + +
Uterque enim summo bono fruitur, id est voluptate.
+ + + +

An tu me de L. Quod autem satis est, eo quicquid accessit, nimium est; Paria sunt igitur. Respondeat totidem verbis.

+ + + +

Ego vero volo in virtute vim esse quam maximam;

+ + + +

Hic ego: Pomponius quidem, inquam, noster iocari videtur, et fortasse suo iure. Simus igitur contenti his. Quod si ita sit, cur opera philosophiae sit danda nescio. Quae iam oratio non a philosopho aliquo, sed a censore opprimenda est. Parvi enim primo ortu sic iacent, tamquam omnino sine animo sint. Nullus est igitur cuiusquam dies natalis.

+ + + +
    +
  • Quid enim dicis omne animal, simul atque sit ortum, applicatum esse ad se diligendum esseque in se conservando occupatum?
  • + + + +
  • Ampulla enim sit necne sit, quis non iure optimo irrideatur, si laboret?
  • + + + +
  • Qui non moveatur et offensione turpitudinis et comprobatione honestatis?
  • + + + +
  • Ergo omni animali illud, quod appetiti positum est in eo, quod naturae est accommodatum.
  • +
+ + + +
Ita graviter et severe voluptatem secrevit a bono.
+ + + +

Nec vero intermittunt aut admirationem earum rerum, quae sunt ab antiquis repertae, aut investigationem novarum. Iam id ipsum absurdum, maximum malum neglegi. Neque enim civitas in seditione beata esse potest nec in discordia dominorum domus; Aliter enim explicari, quod quaeritur, non potest. Animum autem reliquis rebus ita perfecit, ut corpus; Aliud igitur esse censet gaudere, aliud non dolere.

+ + + +
    +
  • Sunt enim quasi prima elementa naturae, quibus ubertas orationis adhiberi vix potest, nec equidem eam cogito consectari.
  • + + + +
  • Praeterea et appetendi et refugiendi et omnino rerum gerendarum initia proficiscuntur aut a voluptate aut a dolore.
  • + + + +
  • Parvi enim primo ortu sic iacent, tamquam omnino sine animo sint.
  • + + + +
  • Atqui, inquam, Cato, si istud optinueris, traducas me ad te totum licebit.
  • +
+ + + +
Sed in rebus apertissimis nimium longi sumus.
+ + + +

Itaque sensibus rationem adiunxit et ratione effecta sensus non reliquit. Verba tu fingas et ea dicas, quae non sentias? Illud dico, ea, quae dicat, praeclare inter se cohaerere. Cupiditates non Epicuri divisione finiebat, sed sua satietate. At eum nihili facit; Tum ille timide vel potius verecunde: Facio, inquit. Qua tu etiam inprudens utebare non numquam.

+ + + +

Zenonis est, inquam, hoc Stoici. Sint modo partes vitae beatae. Alterum significari idem, ut si diceretur, officia media omnia aut pleraque servantem vivere. Sine ea igitur iucunde negat posse se vivere? Nihil opus est exemplis hoc facere longius.

+ + + +
    +
  • Sic, et quidem diligentius saepiusque ista loquemur inter nos agemusque communiter.
  • + + + +
  • Dolere malum est: in crucem qui agitur, beatus esse non potest.
  • + + + +
  • Sed ad bona praeterita redeamus.
  • + + + +
  • Eodem modo is enim tibi nemo dabit, quod, expetendum sit, id esse laudabile.
  • + + + +
  • Quid, cum volumus nomina eorum, qui quid gesserint, nota nobis esse, parentes, patriam, multa praeterea minime necessaria?
  • +
+ + + +

Quid censes in Latino fore? Haec et tu ita posuisti, et verba vestra sunt. Respondeat totidem verbis. Quis est, qui non oderit libidinosam, protervam adolescentiam? Illum mallem levares, quo optimum atque humanissimum virum, Cn. Non laboro, inquit, de nomine. Sed quod proximum fuit non vidit. Sed ad bona praeterita redeamus. Equidem, sed audistine modo de Carneade? Quid turpius quam sapientis vitam ex insipientium sermone pendere? Nam quibus rebus efficiuntur voluptates, eae non sunt in potestate sapientis.

+ + + +

Sed quia studebat laudi et dignitati, multum in virtute processerat. Hoc enim constituto in philosophia constituta sunt omnia. Teneo, inquit, finem illi videri nihil dolere. Nam de isto magna dissensio est. Sed erat aequius Triarium aliquid de dissensione nostra iudicare.

+ + + +

Suo genere perveniant ad extremum; Quicquid porro animo cernimus, id omne oritur a sensibus; Potius ergo illa dicantur: turpe esse, viri non esse debilitari dolore, frangi, succumbere. Duarum enim vitarum nobis erunt instituta capienda. Nam ista vestra: Si gravis, brevis; Cenasti in vita numquam bene, cum omnia in ista Consumis squilla atque acupensere cum decimano. Sed quia studebat laudi et dignitati, multum in virtute processerat. Sine ea igitur iucunde negat posse se vivere?

+ + + +

Non igitur de improbo, sed de callido improbo quaerimus, qualis Q. Et ais, si una littera commota sit, fore tota ut labet disciplina. Ut enim consuetudo loquitur, id solum dicitur honestum, quod est populari fama gloriosum. Habes, inquam, Cato, formam eorum, de quibus loquor, philosophorum. Eorum enim omnium multa praetermittentium, dum eligant aliquid, quod sequantur, quasi curta sententia; Quis hoc dicit? At iam decimum annum in spelunca iacet. Haec quo modo conveniant, non sane intellego. Consequentia exquirere, quoad sit id, quod volumus, effectum. Nos paucis ad haec additis finem faciamus aliquando;

+ + + +
    +
  • In quibus doctissimi illi veteres inesse quiddam caeleste et divinum putaverunt.
  • + + + +
  • Graccho, eius fere, aequalí?
  • + + + +
  • Quacumque enim ingredimur, in aliqua historia vestigium ponimus.
  • +
+ + + +
+

+ Idque testamento cavebit is, qui nobis quasi oraculum ediderit nihil post mortem ad nos pertinere? +

+
+ + + +

Ita relinquet duas, de quibus etiam atque etiam consideret.

+ + + +

Scaevola tribunus plebis ferret ad plebem vellentne de ea re quaeri. Quid censes in Latino fore? Paria sunt igitur. Conclusum est enim contra Cyrenaicos satis acute, nihil ad Epicurum. Conferam tecum, quam cuique verso rem subicias; Summum ením bonum exposuit vacuitatem doloris;

+ + + +

Tubulo putas dicere?

+ + + +

Et quod est munus, quod opus sapientiae? Quae hic rei publicae vulnera inponebat, eadem ille sanabat. Sed ne, dum huic obsequor, vobis molestus sim. Ex rebus enim timiditas, non ex vocabulis nascitur. Hic Speusippus, hic Xenocrates, hic eius auditor Polemo, cuius illa ipsa sessio fuit, quam videmus. Bona autem corporis huic sunt, quod posterius posui, similiora. Miserum hominem! Si dolor summum malum est, dici aliter non potest.

+ + + +

Occultum facinus esse potuerit, gaudebit; Idem iste, inquam, de voluptate quid sentit? Bestiarum vero nullum iudicium puto. Ergo et avarus erit, sed finite, et adulter, verum habebit modum, et luxuriosus eodem modo. Si alia sentit, inquam, alia loquitur, numquam intellegam quid sentiat; Huius ego nunc auctoritatem sequens idem faciam. Uterque enim summo bono fruitur, id est voluptate. Quid, quod homines infima fortuna, nulla spe rerum gerendarum, opifices denique delectantur historia? Non est igitur voluptas bonum. Non enim iam stirpis bonum quaeret, sed animalis. Cur, nisi quod turpis oratio est?

+ + + +

Quae hic rei publicae vulnera inponebat, eadem ille sanabat. Quis istum dolorem timet? Quamvis enim depravatae non sint, pravae tamen esse possunt. Estne, quaeso, inquam, sitienti in bibendo voluptas? Haec igitur Epicuri non probo, inquam. Ea, quae dialectici nunc tradunt et docent, nonne ab illis instituta sunt aut inventa sunt? Quod eo liquidius faciet, si perspexerit rerum inter eas verborumne sit controversia. Quid vero?

+ + + +

Sed ad bona praeterita redeamus. Polemoni et iam ante Aristoteli ea prima visa sunt, quae paulo ante dixi. Sit sane ista voluptas. Ego quoque, inquit, didicerim libentius si quid attuleris, quam te reprehenderim. Sed ad bona praeterita redeamus. Sed nimis multa. Sed quid sentiat, non videtis. Quamquam haec quidem praeposita recte et reiecta dicere licebit.

+ + + +

Cuius etiam illi hortuli propinqui non memoriam solum mihi afferunt, sed ipsum videntur in conspectu meo ponere. Et tamen ego a philosopho, si afferat eloquentiam, non asperner, si non habeat, non admodum flagitem. Haec dicuntur fortasse ieiunius; Verba tu fingas et ea dicas, quae non sentias? Hoc est non modo cor non habere, sed ne palatum quidem. Ergo adhuc, quantum equidem intellego, causa non videtur fuisse mutandi nominis. Quamquam haec quidem praeposita recte et reiecta dicere licebit. Sed residamus, inquit, si placet.

+ + + +

Graecum enim hunc versum nostis omnes-: Suavis laborum est praeteritorum memoria. Idemne potest esse dies saepius, qui semel fuit? Rapior illuc, revocat autem Antiochus, nec est praeterea, quem audiamus. Ita cum ea volunt retinere, quae superiori sententiae conveniunt, in Aristonem incidunt; Aliter enim explicari, quod quaeritur, non potest. Non quam nostram quidem, inquit Pomponius iocans; Memini vero, inquam;

+ + + +
+

+ Ita multo sanguine profuso in laetitia et in victoria est mortuus. +

+
+ + + +

Sed residamus, inquit, si placet.

+ + + +

Itaque in rebus minime obscuris non multus est apud eos disserendi labor. Si quae forte-possumus. Itaque sensibus rationem adiunxit et ratione effecta sensus non reliquit. Si id dicis, vicimus. Poterat autem inpune; Erit enim mecum, si tecum erit. Immo vero, inquit, ad beatissime vivendum parum est, ad beate vero satis. Inde igitur, inquit, ordiendum est. Quis contra in illa aetate pudorem, constantiam, etiamsi sua nihil intersit, non tamen diligat?

+ + + +

Universa enim illorum ratione cum tota vestra confligendum puto. Quasi vero, inquit, perpetua oratio rhetorum solum, non etiam philosophorum sit. Si sapiens, ne tum quidem miser, cum ab Oroete, praetore Darei, in crucem actus est. Sin dicit obscurari quaedam nec apparere, quia valde parva sint, nos quoque concedimus; Est enim effectrix multarum et magnarum voluptatum.

+ + + +

Quid enim de amicitia statueris utilitatis causa expetenda vides.

+ + + +

Nihilo beatiorem esse Metellum quam Regulum. Virtutibus igitur rectissime mihi videris et ad consuetudinem nostrae orationis vitia posuisse contraria. Intellegi quidem, ut propter aliam quampiam rem, verbi gratia propter voluptatem, nos amemus; Ergo ita: non posse honeste vivi, nisi honeste vivatur? Sedulo, inquam, faciam. Ita credo. Sed tamen intellego quid velit. Quo igitur, inquit, modo?

+ + + +

Bonum negas esse divitias, praeposìtum esse dicis? Ita cum ea volunt retinere, quae superiori sententiae conveniunt, in Aristonem incidunt; Eam si varietatem diceres, intellegerem, ut etiam non dicente te intellego; Quid est enim aliud esse versutum? Atqui iste locus est, Piso, tibi etiam atque etiam confirmandus, inquam; Quis tibi ergo istud dabit praeter Pyrrhonem, Aristonem eorumve similes, quos tu non probas? Sin aliud quid voles, postea. Si verbum sequimur, primum longius verbum praepositum quam bonum.

+ + + +

Parvi enim primo ortu sic iacent, tamquam omnino sine animo sint.

+ + + +

Illud urgueam, non intellegere eum quid sibi dicendum sit, cum dolorem summum malum esse dixerit. Qui-vere falsone, quaerere mittimus-dicitur oculis se privasse; Theophrasti igitur, inquit, tibi liber ille placet de beata vita? Quid, si etiam iucunda memoria est praeteritorum malorum?

+ + + +
    +
  • Atque his de rebus et splendida est eorum et illustris oratio.
  • + + + +
  • Cupiditates non Epicuri divisione finiebat, sed sua satietate.
  • + + + +
  • Atque his de rebus et splendida est eorum et illustris oratio.
  • + + + +
  • Etenim si delectamur, cum scribimus, quis est tam invidus, qui ab eo nos abducat?
  • + + + +
  • Sed quid minus probandum quam esse aliquem beatum nec satis beatum?
  • +
+ + + +
+

+ Sin autem ad animum, falsum est, quod negas animi ullum esse gaudium, quod non referatur ad corpus. +

+
+ + + +

Si enim ad populum me vocas, eum.

+ + + +

Non autem hoc: igitur ne illud quidem. Sed finge non solum callidum eum, qui aliquid improbe faciat, verum etiam praepotentem, ut M. Nulla erit controversia. Cum autem in quo sapienter dicimus, id a primo rectissime dicitur. Bonum incolumis acies: misera caecitas. Iam id ipsum absurdum, maximum malum neglegi. Tu enim ista lenius, hic Stoicorum more nos vexat.

+ + + +

Itaque a sapientia praecipitur se ipsam, si usus sit, sapiens ut relinquat. At ille non pertimuit saneque fidenter: Istis quidem ipsis verbis, inquit; Aliam vero vim voluptatis esse, aliam nihil dolendi, nisi valde pertinax fueris, concedas necesse est. Qui ita affectus, beatum esse numquam probabis; Facit enim ille duo seiuncta ultima bonorum, quae ut essent vera, coniungi debuerunt; Quamquam in hac divisione rem ipsam prorsus probo, elegantiam desidero.

+ + + +

Quid de Platone aut de Democrito loquar?

+ + + +
+ + + +

Ita enim vivunt quidam, ut eorum vita refellatur oratio. Quid enim me prohiberet Epicureum esse, si probarem, quae ille diceret? Scientiam pollicentur, quam non erat mirum sapientiae cupido patria esse cariorem. Claudii libidini, qui tum erat summo ne imperio, dederetur.

+ + + +

Quid ergo aliud intellegetur nisi uti ne quae pars naturae neglegatur?

+ + + +

Etiam beatissimum? Gloriosa ostentatio in constituendo summo bono. Parvi enim primo ortu sic iacent, tamquam omnino sine animo sint. Haec quo modo conveniant, non sane intellego. Cum id fugiunt, re eadem defendunt, quae Peripatetici, verba. Res enim se praeclare habebat, et quidem in utraque parte.

+ + + +

Videsne quam sit magna dissensio?

+ + + +

Nunc haec primum fortasse audientis servire debemus. Habent enim et bene longam et satis litigiosam disputationem. Mihi enim satis est, ipsis non satis. Nam illud quidem adduci vix possum, ut ea, quae senserit ille, tibi non vera videantur.

+ + + +
    +
  • Atqui reperies, inquit, in hoc quidem pertinacem;
  • + + + +
  • Contemnit enim disserendi elegantiam, confuse loquitur.
  • + + + +
  • Neque solum ea communia, verum etiam paria esse dixerunt.
  • +
+ + + +

Verum hoc idem saepe faciamus. Nam Pyrrho, Aristo, Erillus iam diu abiecti. Quicquid porro animo cernimus, id omne oritur a sensibus; Octavio fuit, cum illam severitatem in eo filio adhibuit, quem in adoptionem D. Quare si potest esse beatus is, qui est in asperis reiciendisque rebus, potest is quoque esse. Sic enim censent, oportunitatis esse beate vivere. Varietates autem iniurasque fortunae facile veteres philosophorum praeceptis instituta vita superabat. In qua quid est boni praeter summam voluptatem, et eam sempiternam?

+ + + +

Quid enim me prohiberet Epicureum esse, si probarem, quae ille diceret?

+ + + +

Ad quorum et cognitionem et usum iam corroborati natura ipsa praeeunte deducimur. Apparet statim, quae sint officia, quae actiones. Cum id quoque, ut cupiebat, audivisset, evelli iussit eam, qua erat transfixus, hastam. Sed ne, dum huic obsequor, vobis molestus sim. Verum tamen cum de rebus grandioribus dicas, ipsae res verba rapiunt; Saepe ab Aristotele, a Theophrasto mirabiliter est laudata per se ipsa rerum scientia; Philosophi autem in suis lectulis plerumque moriuntur.

+ + + +

Quae cum magnifice primo dici viderentur, considerata minus probabantur. Atque haec coniunctio confusioque virtutum tamen a philosophis ratione quadam distinguitur. Ratio quidem vestra sic cogit. Si quicquam extra virtutem habeatur in bonis. Longum est enim ad omnia respondere, quae a te dicta sunt.

+ + + +
+

+ Nullis enim partitionibus, nullis definitionibus utuntur ipsique dicunt ea se modo probare, quibus natura tacita adsentiatur. +

+
+ + + +

Sit hoc ultimum bonorum, quod nunc a me defenditur; Sin te auctoritas commovebat, nobisne omnibus et Platoni ipsi nescio quem illum anteponebas? Ex rebus enim timiditas, non ex vocabulis nascitur. Primum cur ista res digna odio est, nisi quod est turpis?

+ + + +

Atqui iste locus est, Piso, tibi etiam atque etiam confirmandus, inquam; Hoc etsi multimodis reprehendi potest, tamen accipio, quod dant. Sed potestne rerum maior esse dissensio? Sunt enim prima elementa naturae, quibus auctis vírtutis quasi germen efficitur.

+ + + +

Tamen a proposito, inquam, aberramus. Quare attendo te studiose et, quaecumque rebus iis, de quibus hic sermo est, nomina inponis, memoriae mando; Cur, nisi quod turpis oratio est? Eam si varietatem diceres, intellegerem, ut etiam non dicente te intellego; Cum autem venissemus in Academiae non sine causa nobilitata spatia, solitudo erat ea, quam volueramus. Est enim effectrix multarum et magnarum voluptatum. Atqui iste locus est, Piso, tibi etiam atque etiam confirmandus, inquam; Erat enim res aperta. Sed tempus est, si videtur, et recta quidem ad me.

+ + + +
+

+ Atque ut reliqui fures earum rerum, quas ceperunt, signa commutant, sic illi, ut sententiis nostris pro suis uterentur, nomina tamquam rerum notas mutaverunt. +

+
+ + + +

Sequitur disserendi ratio cognitioque naturae; Illa sunt similia: hebes acies est cuipiam oculorum, corpore alius senescit; Idemne, quod iucunde?

+ + + +

Aliud igitur esse censet gaudere, aliud non dolere.

+ + + +

Unum est sine dolore esse, alterum cum voluptate. At miser, si in flagitiosa et vitiosa vita afflueret voluptatibus. Vide, quantum, inquam, fallare, Torquate. Portenta haec esse dicit, neque ea ratione ullo modo posse vivi; Ergo infelix una molestia, fellx rursus, cum is ipse anulus in praecordiis piscis inventus est? Ac ne plura complectar-sunt enim innumerabilia-, bene laudata virtus voluptatis aditus intercludat necesse est. Nec mihi illud dixeris: Haec enim ipsa mihi sunt voluptati, et erant illa Torquatis. Quodsi ipsam honestatem undique pertectam atque absolutam.

+ + + +

Nos quidem Virtutes sic natae sumus, ut tibi serviremus, aliud negotii nihil habemus. Bestiarum vero nullum iudicium puto. Est autem etiam actio quaedam corporis, quae motus et status naturae congruentis tenet; Nondum autem explanatum satis, erat, quid maxime natura vellet. Illis videtur, qui illud non dubitant bonum dicere -;

+ + + +
+

+ Idque testamento cavebit is, qui nobis quasi oraculum ediderit nihil post mortem ad nos pertinere? +

+
+ + + +

Respondeat totidem verbis. Nihilo beatiorem esse Metellum quam Regulum. Non igitur de improbo, sed de callido improbo quaerimus, qualis Q. Quid de Platone aut de Democrito loquar? Nobis Heracleotes ille Dionysius flagitiose descivisse videtur a Stoicis propter oculorum dolorem. Sed ad bona praeterita redeamus. De quibus cupio scire quid sentias. Laboro autem non sine causa; Quae quidem sapientes sequuntur duce natura tamquam videntes; Diodorus, eius auditor, adiungit ad honestatem vacuitatem doloris. Octavio fuit, cum illam severitatem in eo filio adhibuit, quem in adoptionem D.

+ + + +

An me, inquam, nisi te audire vellem, censes haec dicturum fuisse? Cupit enim dícere nihil posse ad beatam vitam deesse sapienti. Cur id non ita fit? Quod ea non occurrentia fingunt, vincunt Aristonem; At certe gravius. Nihil illinc huc pervenit. Quae cum magnifice primo dici viderentur, considerata minus probabantur. Universa enim illorum ratione cum tota vestra confligendum puto.

+ + + +

Sumenda potius quam expetenda. Quare conare, quaeso. Murenam te accusante defenderem. Quantum Aristoxeni ingenium consumptum videmus in musicis? Illa sunt similia: hebes acies est cuipiam oculorum, corpore alius senescit; At multis malis affectus. Sic enim censent, oportunitatis esse beate vivere.

+ + + +

Laboro autem non sine causa; Ita ne hoc quidem modo paria peccata sunt. Quis istum dolorem timet? Primum in nostrane potestate est, quid meminerimus? Quae diligentissime contra Aristonem dicuntur a Chryippo.

+ + + +

Id Sextilius factum negabat. Audeo dicere, inquit.

+ + + +

Quia nec honesto quic quam honestius nec turpi turpius. Quod cum dixissent, ille contra. Sed tu istuc dixti bene Latine, parum plane. Sic vester sapiens magno aliquo emolumento commotus cicuta, si opus erit, dimicabit. Quae quo sunt excelsiores, eo dant clariora indicia naturae. Non igitur potestis voluptate omnia dirigentes aut tueri aut retinere virtutem. Certe non potest. Ut in geometria, prima si dederis, danda sunt omnia.

+ + + +

Urgent tamen et nihil remittunt. Non dolere, inquam, istud quam vim habeat postea videro; Tria genera cupiditatum, naturales et necessariae, naturales et non necessariae, nec naturales nec necessariae. Atque haec ita iustitiae propria sunt, ut sint virtutum reliquarum communia. Ita fit cum gravior, tum etiam splendidior oratio. Tum Quintus: Est plane, Piso, ut dicis, inquit. Etenim si delectamur, cum scribimus, quis est tam invidus, qui ab eo nos abducat? Post enim Chrysippum eum non sane est disputatum.

+ + + +

Quod equidem non reprehendo; Quid de Platone aut de Democrito loquar? Non enim ipsa genuit hominem, sed accepit a natura inchoatum. Urgent tamen et nihil remittunt. At ego quem huic anteponam non audeo dicere; Quid ei reliquisti, nisi te, quoquo modo loqueretur, intellegere, quid diceret?

+ + + +

Ut in geometria, prima si dederis, danda sunt omnia.

+ + + +

Aliter enim nosmet ipsos nosse non possumus. Ne amores quidem sanctos a sapiente alienos esse arbitrantur. Quamquam haec quidem praeposita recte et reiecta dicere licebit. Sed residamus, inquit, si placet. Ut non sine causa ex iis memoriae ducta sit disciplina. Ita relinquet duas, de quibus etiam atque etiam consideret. Mihi enim satis est, ipsis non satis. Respondent extrema primis, media utrisque, omnia omnibus.

+ + + +

Aeque enim contingit omnibus fidibus, ut incontentae sint. Obsecro, inquit, Torquate, haec dicit Epicurus? Quam ob rem tandem, inquit, non satisfacit? Quod praeceptum quia maius erat, quam ut ab homine videretur, idcirco assignatum est deo. Quid ergo aliud intellegetur nisi uti ne quae pars naturae neglegatur? Et ais, si una littera commota sit, fore tota ut labet disciplina. Est enim effectrix multarum et magnarum voluptatum. Sed tamen intellego quid velit. Qui non moveatur et offensione turpitudinis et comprobatione honestatis? Minime vero istorum quidem, inquit. Illa videamus, quae a te de amicitia dicta sunt.

+ + + +

Teneo, inquit, finem illi videri nihil dolere. Suo enim quisque studio maxime ducitur. Possumusne ergo in vita summum bonum dicere, cum id ne in cena quidem posse videamur? Aliter autem vobis placet. Optime, inquam. Ne amores quidem sanctos a sapiente alienos esse arbitrantur.

+ + + +
+

+ Hoc autem loco tantum explicemus haec honesta, quae dico, praeterquam quod nosmet ipsos diligamus, praeterea suapte natura per se esse expetenda. +

+
+ + + +

Cum audissem Antiochum, Brute, ut solebam, cum M. Dat enim intervalla et relaxat. Numquam facies. Totum autem id externum est, et quod externum, id in casu est. Iubet igitur nos Pythius Apollo noscere nosmet ipsos. Bonum liberi: misera orbitas.

+ + + +

Quis enim est, qui non videat haec esse in natura rerum tria? Quae cum praeponunt, ut sit aliqua rerum selectio, naturam videntur sequi; Et quidem iure fortasse, sed tamen non gravissimum est testimonium multitudinis. Cum sciret confestim esse moriendum eamque mortem ardentiore studio peteret, quam Epicurus voluptatem petendam putat. Nunc omni virtuti vitium contrario nomine opponitur. Istic sum, inquit. Cum praesertim illa perdiscere ludus esset. Et quidem iure fortasse, sed tamen non gravissimum est testimonium multitudinis.

+ + + +

Duarum enim vitarum nobis erunt instituta capienda. Hoc ipsum elegantius poni meliusque potuit. Nam et complectitur verbis, quod vult, et dicit plane, quod intellegam; Optime, inquam. Ita credo. Ait enim se, si uratur, Quam hoc suave! dicturum. Itaque hic ipse iam pridem est reiectus;

+ + + +
Compensabatur, inquit, cum summis doloribus laetitia.
+ + + +

Cur tantas regiones barbarorum pedibus obiit, tot maria transmisit? Dic in quovis conventu te omnia facere, ne doleas. Rationis enim perfectio est virtus; Paulum, cum regem Persem captum adduceret, eodem flumine invectio? Id enim natura desiderat.

+ + + +
    +
  • Neque solum ea communia, verum etiam paria esse dixerunt.
  • + + + +
  • Qui autem de summo bono dissentit de tota philosophiae ratione dissentit.
  • + + + +
  • Non enim, si omnia non sequebatur, idcirco non erat ortus illinc.
  • + + + +
  • Ut nemo dubitet, eorum omnia officia quo spectare, quid sequi, quid fugere debeant?
  • + + + +
  • Ab hoc autem quaedam non melius quam veteres, quaedam omnino relicta.
  • +
+ + + +
+

+ Quod enim testimonium maius quaerimus, quae honesta et recta sint, ipsa esse optabilia per sese, cum videamus tanta officia morientis? +

+
+ + + +
+

+ Quod idem Peripatetici non tenent, quibus dicendum est, quae et honesta actio sit et sine dolore, eam magis esse expetendam, quam si esset eadem actio cum dolore. +

+
+ + + +
    +
  • Quid est, quod ab ea absolvi et perfici debeat?
  • + + + +
  • Hoc loco discipulos quaerere videtur, ut, qui asoti esse velint, philosophi ante fiant.
  • + + + +
  • Omnis enim est natura diligens sui.
  • +
+ + + +

Praeclare hoc quidem. Id et fieri posse et saepe esse factum et ad voluptates percipiendas maxime pertinere. Quicquid porro animo cernimus, id omne oritur a sensibus; Quae animi affectio suum cuique tribuens atque hanc, quam dico. At ille non pertimuit saneque fidenter: Istis quidem ipsis verbis, inquit; Quasi vero, inquit, perpetua oratio rhetorum solum, non etiam philosophorum sit.

+ + + +
+

+ Nec vero id satis est, neminem esse, qui ipse se oderit, sed illud quoque intellegendum est, neminem esse, qui,quo modo se habeat, nihil sua censeat inte resse. +

+
+ + + +

Paupertas si malum est, mendicus beatus esse nemo potest, quamvis sit sapiens.

+ + + +

Nunc vides, quid faciat. Multa sunt dicta ab antiquis de contemnendis ac despiciendis rebus humanis; Ita relinquet duas, de quibus etiam atque etiam consideret. Vide, ne etiam menses! nisi forte eum dicis, qui, simul atque arripuit, interficit.

+ + + +

Cum id fugiunt, re eadem defendunt, quae Peripatetici, verba. Ut pulsi recurrant? Isto modo, ne si avia quidem eius nata non esset. Cum audissem Antiochum, Brute, ut solebam, cum M. Apparet statim, quae sint officia, quae actiones. Non est enim vitium in oratione solum, sed etiam in moribus. Frater et T. Graecis hoc modicum est: Leonidas, Epaminondas, tres aliqui aut quattuor; Transfer idem ad modestiam vel temperantiam, quae est moderatio cupiditatum rationi oboediens.

+ + + +
+

+ Is cum arderet podagrae doloribus visitassetque hominem Charmides Epicureus perfamiliaris et tristis exiret, Mane, quaeso, inquit, Charmide noster; +

+
+ + + +

Non laboro, inquit, de nomine.

+ + + +

Dic in quovis conventu te omnia facere, ne doleas. Tu quidem reddes; Certe, nisi voluptatem tanti aestimaretis. Terram, mihi crede, ea lanx et maria deprimet. Quantam rem agas, ut Circeis qui habitet totum hunc mundum suum municipium esse existimet? Si enim ita est, vide ne facinus facias, cum mori suadeas.

+ + + +
Ex quo illud efficitur, qui bene cenent omnis libenter cenare, qui libenter, non continuo bene.
+ + + +

Dicet pro me ipsa virtus nec dubitabit isti vestro beato M. Hoc dixerit potius Ennius: Nimium boni est, cui nihil est mali. Frater et T. Nam quid possumus facere melius? Qualis ista philosophia est, quae non interitum afferat pravitatis, sed sit contenta mediocritate vitiorum? Neminem videbis ita laudatum, ut artifex callidus comparandarum voluptatum diceretur. An eum discere ea mavis, quae cum plane perdidiceriti nihil sciat? At multis se probavit.

+ + + +

Quis enim est, qui non videat haec esse in natura rerum tria? Ergo, si semel tristior effectus est, hilara vita amissa est? Eaedem res maneant alio modo. Quid enim est a Chrysippo praetermissum in Stoicis? Cum praesertim illa perdiscere ludus esset. Negat enim summo bono afferre incrementum diem. Utilitatis causa amicitia est quaesita.

+ + + +

Transfer idem ad modestiam vel temperantiam, quae est moderatio cupiditatum rationi oboediens.

+ + + +

Hoc ne statuam quidem dicturam pater aiebat, si loqui posset. In qua quid est boni praeter summam voluptatem, et eam sempiternam?

+ + + +
    +
  • Intrandum est igitur in rerum naturam et penitus quid ea postulet pervidendum;
  • + + + +
  • Cuius quidem, quoniam Stoicus fuit, sententia condemnata mihi videtur esse inanitas ista verborum.
  • + + + +
  • Quid est, quod ab ea absolvi et perfici debeat?
  • + + + +
  • Rationis enim perfectio est virtus;
  • +
+ + + +

Aliter homines, aliter philosophos loqui putas oportere?

+ + + +

Si est nihil nisi corpus, summa erunt illa: valitudo, vacuitas doloris, pulchritudo, cetera. Quid autem habent admirationis, cum prope accesseris? Itaque his sapiens semper vacabit.

+ + + +

Tecum optime, deinde etiam cum mediocri amico. Illud dico, ea, quae dicat, praeclare inter se cohaerere. Quid de Platone aut de Democrito loquar? In qua quid est boni praeter summam voluptatem, et eam sempiternam? Itaque vides, quo modo loquantur, nova verba fingunt, deserunt usitata. Qua ex cognitione facilior facta est investigatio rerum occultissimarum. Deinde disputat, quod cuiusque generis animantium statui deceat extremum. Stoici scilicet. Ne amores quidem sanctos a sapiente alienos esse arbitrantur. Quantum Aristoxeni ingenium consumptum videmus in musicis?

+ + + +
Quis contra in illa aetate pudorem, constantiam, etiamsi sua nihil intersit, non tamen diligat?
+ + + +

Tamen a proposito, inquam, aberramus. Si longus, levis. Atqui reperies, inquit, in hoc quidem pertinacem; Recte dicis; Sed in rebus apertissimis nimium longi sumus. In qua si nihil est praeter rationem, sit in una virtute finis bonorum;

+ + + +
+

+ Ita fit beatae vitae domina fortuna, quam Epicurus ait exiguam intervenire sapienti. +

+
+ + + +
+

+ Nam illud quidem adduci vix possum, ut ea, quae senserit ille, tibi non vera videantur. +

+
+ + + +

Iis igitur est difficilius satis facere, qui se Latina scripta dicunt contemnere.

+ + + +

Quid ait Aristoteles reliquique Platonis alumni? Quo studio Aristophanem putamus aetatem in litteris duxisse? Hoc enim constituto in philosophia constituta sunt omnia. Ergo instituto veterum, quo etiam Stoici utuntur, hinc capiamus exordium. Hanc quoque iucunditatem, si vis, transfer in animum; Alia quaedam dicent, credo, magna antiquorum esse peccata, quae ille veri investigandi cupidus nullo modo ferre potuerit. Omnis enim est natura diligens sui. Illa sunt similia: hebes acies est cuipiam oculorum, corpore alius senescit;

+ + + +

Qui enim existimabit posse se miserum esse beatus non erit. Sextilio Rufo, cum is rem ad amicos ita deferret, se esse heredem Q. Quare si potest esse beatus is, qui est in asperis reiciendisque rebus, potest is quoque esse. Non est enim vitium in oratione solum, sed etiam in moribus. Quod cum dixissent, ille contra. Ut scias me intellegere, primum idem esse dico voluptatem, quod ille don. Quo studio Aristophanem putamus aetatem in litteris duxisse? Et si turpitudinem fugimus in statu et motu corporis, quid est cur pulchritudinem non sequamur?

+ + + +
    +
  • Laelius clamores sofòw ille so lebat Edere compellans gumias ex ordine nostros.
  • + + + +
  • Ut proverbia non nulla veriora sint quam vestra dogmata.
  • + + + +
  • Quamquam ab iis philosophiam et omnes ingenuas disciplinas habemus;
  • + + + +
  • Illud non continuo, ut aeque incontentae.
  • + + + +
  • Illa sunt similia: hebes acies est cuipiam oculorum, corpore alius senescit;
  • + + + +
  • Hoc dixerit potius Ennius: Nimium boni est, cui nihil est mali.
  • +
+ + + +

Bonum integritas corporis: misera debilitas. Dici enim nihil potest verius. Paulum, cum regem Persem captum adduceret, eodem flumine invectio? An est aliquid per se ipsum flagitiosum, etiamsi nulla comitetur infamia? Sin tantum modo ad indicia veteris memoriae cognoscenda, curiosorum. Ut id aliis narrare gestiant? Negare non possum. Graecum enim hunc versum nostis omnes-: Suavis laborum est praeteritorum memoria. Deque his rebus satis multa in nostris de re publica libris sunt dicta a Laelio.

+ + + +

Primum in nostrane potestate est, quid meminerimus? Etiam beatissimum? Qua tu etiam inprudens utebare non numquam. Sit enim idem caecus, debilis. Qui autem diffidet perpetuitati bonorum suorum, timeat necesse est, ne aliquando amissis illis sit miser. Ratio quidem vestra sic cogit.

+ + + +

Cur ipse Pythagoras et Aegyptum lustravit et Persarum magos adiit? Quae contraria sunt his, malane? Nihil acciderat ei, quod nollet, nisi quod anulum, quo delectabatur, in mari abiecerat.

+ + + +
+

+ Epicurei num desistunt de isdem, de quibus et ab Epicuro scriptum est et ab antiquis, ad arbitrium suum scribere? +

+
+ + + +

Immo videri fortasse. His singulis copiose responderi solet, sed quae perspicua sunt longa esse non debent. Septem autem illi non suo, sed populorum suffragio omnium nominati sunt. Tum Piso: Quoniam igitur aliquid omnes, quid Lucius noster? Nec vero alia sunt quaerenda contra Carneadeam illam sententiam. Nam Pyrrho, Aristo, Erillus iam diu abiecti. Nihil acciderat ei, quod nollet, nisi quod anulum, quo delectabatur, in mari abiecerat.

+ + + +
    +
  • Quid enim est a Chrysippo praetermissum in Stoicis?
  • + + + +
  • Quo modo autem philosophus loquitur?
  • + + + +
  • Qui non moveatur et offensione turpitudinis et comprobatione honestatis?
  • +
+ + + +
Eadem nunc mea adversum te oratio est.
+ + + +

Aliter enim explicari, quod quaeritur, non potest. Iam doloris medicamenta illa Epicurea tamquam de narthecio proment: Si gravis, brevis; Haec para/doca illi, nos admirabilia dicamus. Sit enim idem caecus, debilis. Facile est hoc cernere in primis puerorum aetatulis. Respondent extrema primis, media utrisque, omnia omnibus. De malis autem et bonis ab iis animalibus, quae nondum depravata sint, ait optime iudicari. Apparet statim, quae sint officia, quae actiones.

+ + + +

Suo genere perveniant ad extremum;

+ + + +

Nam et complectitur verbis, quod vult, et dicit plane, quod intellegam; Non quam nostram quidem, inquit Pomponius iocans; Nec vero sum nescius esse utilitatem in historia, non modo voluptatem. Res enim se praeclare habebat, et quidem in utraque parte. Cur iustitia laudatur? Istam voluptatem, inquit, Epicurus ignorat? Nunc ita separantur, ut disiuncta sint, quo nihil potest esse perversius.

+ + + +

Expectoque quid ad id, quod quaerebam, respondeas. Ita multa dicunt, quae vix intellegam. Vide, quantum, inquam, fallare, Torquate. Quid adiuvas? Quaesita enim virtus est, non quae relinqueret naturam, sed quae tueretur. Eam tum adesse, cum dolor omnis absit; Animum autem reliquis rebus ita perfecit, ut corpus; Nulla profecto est, quin suam vim retineat a primo ad extremum.

+ + + +
+

+ Quod quidem pluris est haud paulo magisque ipsum propter se expetendum quam aut sensus aut corporis ea, quae diximus, quibus tantum praestat mentis excellens perfectio, ut vix cogitari possit quid intersit. +

+
+ + + +

Non dolere, inquam, istud quam vim habeat postea videro; Ut proverbia non nulla veriora sint quam vestra dogmata. Sed quid attinet de rebus tam apertis plura requirere?

+ + + +
+

+ Cognitis autem rerum finibus, cum intellegitur, quid sit et bonorum extremum et malorum, inventa vitae via est conformatioque omnium officiorum, cum quaeritur, quo quodque referatur; +

+
+ + + +

Ergo ita: non posse honeste vivi, nisi honeste vivatur? Itaque hic ipse iam pridem est reiectus; Mihi enim satis est, ipsis non satis. Ita ne hoc quidem modo paria peccata sunt. Qui autem esse poteris, nisi te amor ipse ceperit? Commoda autem et incommoda in eo genere sunt, quae praeposita et reiecta diximus; Non quaeritur autem quid naturae tuae consentaneum sit, sed quid disciplinae. Si id dicis, vicimus. Nondum autem explanatum satis, erat, quid maxime natura vellet.

+ + + +

Sed quid minus probandum quam esse aliquem beatum nec satis beatum?

+ + + +

Fortasse id optimum, sed ubi illud: Plus semper voluptatis? Itaque ab his ordiamur. Summum a vobis bonum voluptas dicitur. Respondeat totidem verbis. Qui potest igitur habitare in beata vita summi mali metus? Quid me istud rogas?

+ + + +

Honesta oratio, Socratica, Platonis etiam. Varietates autem iniurasque fortunae facile veteres philosophorum praeceptis instituta vita superabat. Ratio quidem vestra sic cogit. Quid ergo aliud intellegetur nisi uti ne quae pars naturae neglegatur? Illa sunt similia: hebes acies est cuipiam oculorum, corpore alius senescit; Nec tamen ullo modo summum pecudis bonum et hominis idem mihi videri potest. Quantam rem agas, ut Circeis qui habitet totum hunc mundum suum municipium esse existimet? Nos quidem Virtutes sic natae sumus, ut tibi serviremus, aliud negotii nihil habemus. Itaque hic ipse iam pridem est reiectus; Easdemne res?

+ + + +

Iam contemni non poteris. Incommoda autem et commoda-ita enim estmata et dustmata appello-communia esse voluerunt, paria noluerunt. Urgent tamen et nihil remittunt. Fortemne possumus dicere eundem illum Torquatum? Sit, inquam, tam facilis, quam vultis, comparatio voluptatis, quid de dolore dicemus? Huius ego nunc auctoritatem sequens idem faciam.

+ + + +
    +
  • Hoc Hieronymus summum bonum esse dixit.
  • + + + +
  • Etenim nec iustitia nec amicitia esse omnino poterunt, nisi ipsae per se expetuntur.
  • +
+ + + +

Equidem, sed audistine modo de Carneade? Ita enim vivunt quidam, ut eorum vita refellatur oratio. Multoque hoc melius nos veriusque quam Stoici. Ecce aliud simile dissimile. Sed eum qui audiebant, quoad poterant, defendebant sententiam suam. Que Manilium, ab iisque M. Quasi ego id curem, quid ille aiat aut neget. Quamquam tu hanc copiosiorem etiam soles dicere.

+ + + +

Quo tandem modo? Cur post Tarentum ad Archytam? Quodsi ipsam honestatem undique pertectam atque absolutam. Eadem nunc mea adversum te oratio est. Quodsi ipsam honestatem undique pertectam atque absolutam. Idemque diviserunt naturam hominis in animum et corpus.

+ + + +

Sed quanta sit alias, nunc tantum possitne esse tanta. Haec quo modo conveniant, non sane intellego. Torquatus, is qui consul cum Cn. Quorum altera prosunt, nocent altera. Quae cum praeponunt, ut sit aliqua rerum selectio, naturam videntur sequi; Invidiosum nomen est, infame, suspectum. Eorum enim est haec querela, qui sibi cari sunt seseque diligunt. Duae sunt enim res quoque, ne tu verba solum putes.

+ + + +

Quis Aristidem non mortuum diligit?

+ + + +

Nam, ut sint illa vendibiliora, haec uberiora certe sunt. Urgent tamen et nihil remittunt. Huius, Lyco, oratione locuples, rebus ipsis ielunior. Nihil enim iam habes, quod ad corpus referas; Quid enim me prohiberet Epicureum esse, si probarem, quae ille diceret? Cui Tubuli nomen odio non est? Quamquam te quidem video minime esse deterritum.

+ + + +

Haeret in salebra.

+ + + +

Bonum negas esse divitias, praeposìtum esse dicis? Facete M. Atque hoc loco similitudines eas, quibus illi uti solent, dissimillimas proferebas. Quam nemo umquam voluptatem appellavit, appellat; De malis autem et bonis ab iis animalibus, quae nondum depravata sint, ait optime iudicari. Facit igitur Lucius noster prudenter, qui audire de summo bono potissimum velit; Tollenda est atque extrahenda radicitus. Theophrasti igitur, inquit, tibi liber ille placet de beata vita?

+ + + +

Quamquam in hac divisione rem ipsam prorsus probo, elegantiam desidero. Habes, inquam, Cato, formam eorum, de quibus loquor, philosophorum. Ut pulsi recurrant? Quid turpius quam sapientis vitam ex insipientium sermone pendere? Si quicquam extra virtutem habeatur in bonis. Ergo opifex plus sibi proponet ad formarum quam civis excellens ad factorum pulchritudinem? Perge porro; Ecce aliud simile dissimile. Longum est enim ad omnia respondere, quae a te dicta sunt. Quodsi ipsam honestatem undique pertectam atque absolutam.

+ + + +
+

+ Vides igitur, si amicitiam sua caritate metiare, nihil esse praestantius, sin emolumento, summas familiaritates praediorum fructuosorum mercede superari. +

+
+ + + +

Quae similitudo in genere etiam humano apparet. Disserendi artem nullam habuit. Septem autem illi non suo, sed populorum suffragio omnium nominati sunt. Certe non potest. Similiter sensus, cum accessit ad naturam, tuetur illam quidem, sed etiam se tuetur; An haec ab eo non dicuntur? Summum a vobis bonum voluptas dicitur. Sed haec in pueris; Restinguet citius, si ardentem acceperit.

+ + + +

Negat esse eam, inquit, propter se expetendam.

+ + + +
+ + + +

Eam si varietatem diceres, intellegerem, ut etiam non dicente te intellego; Varietates autem iniurasque fortunae facile veteres philosophorum praeceptis instituta vita superabat. Scio enim esse quosdam, qui quavis lingua philosophari possint; Nam illud quidem adduci vix possum, ut ea, quae senserit ille, tibi non vera videantur. Non igitur bene. Aliter enim nosmet ipsos nosse non possumus. Expectoque quid ad id, quod quaerebam, respondeas.

+ + + +
Si qua in iis corrigere voluit, deteriora fecit.
+ + + +

Non risu potius quam oratione eiciendum? Hoc loco tenere se Triarius non potuit. Sin dicit obscurari quaedam nec apparere, quia valde parva sint, nos quoque concedimus; Is es profecto tu. Tu enim ista lenius, hic Stoicorum more nos vexat.

+ + + +
+

+ Atqui reperiemus asotos primum ita non religiosos, ut edint de patella, deinde ita mortem non timentes, ut illud in ore habeant ex Hymnide: Mihi sex menses satis sunt vitae, septimum Orco spondeo. +

+
+ + + +
    +
  • Quem Tiberina descensio festo illo die tanto gaudio affecit, quanto L.
  • + + + +
  • In quibus doctissimi illi veteres inesse quiddam caeleste et divinum putaverunt.
  • + + + +
  • Omnes enim iucundum motum, quo sensus hilaretur.
  • + + + +
  • Quae cum dixisset paulumque institisset, Quid est?
  • + + + +
  • His enim rebus detractis negat se reperire in asotorum vita quod reprehendat.
  • + + + +
  • Profectus in exilium Tubulus statim nec respondere ausus;
  • + + + +
  • Sin kakan malitiam dixisses, ad aliud nos unum certum vitium consuetudo Latina traduceret.
  • +
+ + + +
    +
  • At cum de plurimis eadem dicit, tum certe de maximis.
  • + + + +
  • Apparet statim, quae sint officia, quae actiones.
  • + + + +
  • Nam si propter voluptatem, quae est ista laus, quae possit e macello peti?
  • + + + +
  • Ut pulsi recurrant?
  • +
+ + + +

Sed tamen enitar et, si minus multa mihi occurrent, non fugiam ista popularia. Idemne, quod iucunde? Ut enim consuetudo loquitur, id solum dicitur honestum, quod est populari fama gloriosum. Beatus sibi videtur esse moriens. Sed plane dicit quod intellegit. Quo modo autem philosophus loquitur?

+ + + +

Dicimus aliquem hilare vivere;

+ + + +

Atqui iste locus est, Piso, tibi etiam atque etiam confirmandus, inquam; Ea possunt paria non esse. Sed ad haec, nisi molestum est, habeo quae velim. Cur deinde Metrodori liberos commendas?

+ + + +

Tu vero, inquam, ducas licet, si sequetur; Nosti, credo, illud: Nemo pius est, qui pietatem-; Cur post Tarentum ad Archytam? At cum de plurimis eadem dicit, tum certe de maximis. Hoc positum in Phaedro a Platone probavit Epicurus sensitque in omni disputatione id fieri oportere. At eum nihili facit; Bestiarum vero nullum iudicium puto. Sapiens autem semper beatus est et est aliquando in dolore;

+ + + +
+

+ Iam vero animus non esse solum, sed etiam cuiusdam modi debet esse, ut et omnis partis suas habeat incolumis et de virtutibus nulla desit. +

+
+ + + +

Et ille ridens: Video, inquit, quid agas; Haec quo modo conveniant, non sane intellego. Hoc tu nunc in illo probas. Summum a vobis bonum voluptas dicitur. Dicet pro me ipsa virtus nec dubitabit isti vestro beato M. Sed quot homines, tot sententiae;

+ + + +

Videamus animi partes, quarum est conspectus illustrior; Age, inquies, ista parva sunt. Cur deinde Metrodori liberos commendas? Nosti, credo, illud: Nemo pius est, qui pietatem-; Nemo igitur esse beatus potest. Nam si quae sunt aliae, falsum est omnis animi voluptates esse e corporis societate. Num igitur eum postea censes anxio animo aut sollicito fuisse? Hoc dixerit potius Ennius: Nimium boni est, cui nihil est mali.

+ + + +

Duarum enim vitarum nobis erunt instituta capienda. Quid est, quod ab ea absolvi et perfici debeat? Tu vero, inquam, ducas licet, si sequetur; Cur tantas regiones barbarorum pedibus obiit, tot maria transmisit? Bonum integritas corporis: misera debilitas. Si quicquam extra virtutem habeatur in bonis.

+ + + +

Illud non continuo, ut aeque incontentae. Ita cum ea volunt retinere, quae superiori sententiae conveniunt, in Aristonem incidunt; Cum praesertim illa perdiscere ludus esset. Ne in odium veniam, si amicum destitero tueri. Deinde disputat, quod cuiusque generis animantium statui deceat extremum. Haec dicuntur inconstantissime.

+ + + +

At quanta conantur! Mundum hunc omnem oppidum esse nostrum! Incendi igitur eos, qui audiunt, vides. Hoc loco tenere se Triarius non potuit. Hoc positum in Phaedro a Platone probavit Epicurus sensitque in omni disputatione id fieri oportere. Quid turpius quam sapientis vitam ex insipientium sermone pendere? Non enim quaero quid verum, sed quid cuique dicendum sit. Quamquam in hac divisione rem ipsam prorsus probo, elegantiam desidero.

+ + + +

Quid ad utilitatem tantae pecuniae? Non quam nostram quidem, inquit Pomponius iocans; At certe gravius. Duarum enim vitarum nobis erunt instituta capienda. Ergo infelix una molestia, fellx rursus, cum is ipse anulus in praecordiis piscis inventus est? Aeque enim contingit omnibus fidibus, ut incontentae sint. Peccata paria. Ita nemo beato beatior. An potest cupiditas finiri? Suo genere perveniant ad extremum;

+ + + +
+

+ Audax negotium, dicerem impudens, nisi hoc institutum postea translatum ad philosophos nostros esset. +

+
+ + + +

Quid turpius quam sapientis vitam ex insipientium sermone pendere?

+ + + +

Deinde disputat, quod cuiusque generis animantium statui deceat extremum. Sed ille, ut dixi, vitiose. Itaque contra est, ac dicitis; Hanc quoque iucunditatem, si vis, transfer in animum; Duo enim genera quae erant, fecit tria. Certe nihil nisi quod possit ipsum propter se iure laudari. Deque his rebus satis multa in nostris de re publica libris sunt dicta a Laelio. Quia nec honesto quic quam honestius nec turpi turpius.

+ + + +

Itaque haec cum illis est dissensio, cum Peripateticis nulla sane. At quicum ioca seria, ut dicitur, quicum arcana, quicum occulta omnia? Ille vero, si insipiens-quo certe, quoniam tyrannus -, numquam beatus; Non dolere, inquam, istud quam vim habeat postea videro; Nam et complectitur verbis, quod vult, et dicit plane, quod intellegam; Non est igitur voluptas bonum.

+ + + +

Nonne videmus quanta perturbatio rerum omnium consequatur, quanta confusio? Deinde prima illa, quae in congressu solemus: Quid tu, inquit, huc? Universa enim illorum ratione cum tota vestra confligendum puto. At miser, si in flagitiosa et vitiosa vita afflueret voluptatibus. Quid, si non sensus modo ei sit datus, verum etiam animus hominis? Restinguet citius, si ardentem acceperit. Nobis Heracleotes ille Dionysius flagitiose descivisse videtur a Stoicis propter oculorum dolorem. Apparet statim, quae sint officia, quae actiones. Summum a vobis bonum voluptas dicitur. Mihi enim erit isdem istis fortasse iam utendum. Ergo adhuc, quantum equidem intellego, causa non videtur fuisse mutandi nominis.

+ + + +
+

+ Etsi hoc quidem est in vitio, dissolutionem naturae tam valde perhorrescere-quod item est reprehendendum in dolore -, sed quia fere sic afficiuntur omnes, satis argomenti est ab interitu naturam abhorrere; +

+
+ + + +
+

+ Perfecto enim et concluso neque virtutibus neque amicitiis usquam locum esse, si ad voluptatem omnia referantur, nihil praeterea est magnopere dicendum. +

+
+ + + +

Solum praeterea formosum, solum liberum, solum civem, stultost; Tibi hoc incredibile, quod beatissimum. Quos quidem tibi studiose et diligenter tractandos magnopere censeo. Sed hoc sane concedamus. An me, inquam, nisi te audire vellem, censes haec dicturum fuisse? Itaque hic ipse iam pridem est reiectus;

+ + + +
+

+ Sapientem locupletat ipsa natura, cuius divitias Epicurus parabiles esse docuit. +

+
+ + + +

Nam et complectitur verbis, quod vult, et dicit plane, quod intellegam; Quodcumque in mentem incideret, et quodcumque tamquam occurreret. Cum id fugiunt, re eadem defendunt, quae Peripatetici, verba. Cur deinde Metrodori liberos commendas? Quamvis enim depravatae non sint, pravae tamen esse possunt. Sed eum qui audiebant, quoad poterant, defendebant sententiam suam. Unum nescio, quo modo possit, si luxuriosus sit, finitas cupiditates habere. Sed quanta sit alias, nunc tantum possitne esse tanta.

+ + + +
+

+ Num igitur utiliorem tibi hunc Triarium putas esse posse, quam si tua sint Puteolis granaria? +

+
+ + + +
+

+ Quo minus animus a se ipse dissidens secumque discordans gustare partem ullam liquidae voluptatis et liberae potest. +

+
+ + + +

Quae diligentissime contra Aristonem dicuntur a Chryippo.

+ + + +

Praeclarae mortes sunt imperatoriae; At certe gravius. Ad eas enim res ab Epicuro praecepta dantur. Confecta res esset. Invidiosum nomen est, infame, suspectum. Non semper, inquam;

+ + + +

Dolor ergo, id est summum malum, metuetur semper, etiamsi non aderit; A mene tu? Varietates autem iniurasque fortunae facile veteres philosophorum praeceptis instituta vita superabat. Quid loquor de nobis, qui ad laudem et ad decus nati, suscepti, instituti sumus?

+ + + +

Nulla profecto est, quin suam vim retineat a primo ad extremum. Intrandum est igitur in rerum naturam et penitus quid ea postulet pervidendum; Sin autem eos non probabat, quid attinuit cum iis, quibuscum re concinebat, verbis discrepare? Quod quidem iam fit etiam in Academia. At enim hic etiam dolore. Inde igitur, inquit, ordiendum est. Sit hoc ultimum bonorum, quod nunc a me defenditur; Mihi, inquam, qui te id ipsum rogavi? Nihilo beatiorem esse Metellum quam Regulum.

+ + + +

Quis est, qui non oderit libidinosam, protervam adolescentiam?

+ + + +

Themistocles quidem, cum ei Simonides an quis alius artem memoriae polliceretur, Oblivionis, inquit, mallem. Comprehensum, quod cognitum non habet? Claudii libidini, qui tum erat summo ne imperio, dederetur. Superiores tres erant, quae esse possent, quarum est una sola defensa, eaque vehementer. Quia nec honesto quic quam honestius nec turpi turpius. Sed tamen est aliquid, quod nobis non liceat, liceat illis. Conferam tecum, quam cuique verso rem subicias; Polemoni et iam ante Aristoteli ea prima visa sunt, quae paulo ante dixi. Invidiosum nomen est, infame, suspectum. Quae quidem vel cum periculo est quaerenda vobis; De ingenio eius in his disputationibus, non de moribus quaeritur. Hoc dixerit potius Ennius: Nimium boni est, cui nihil est mali.

+ + + +

Comprehensum, quod cognitum non habet? Laboro autem non sine causa; Quae cum dixisset paulumque institisset, Quid est? Quoniam, si dis placet, ab Epicuro loqui discimus. Verum hoc idem saepe faciamus. Non potes, nisi retexueris illa. Tertium autem omnibus aut maximis rebus iis, quae secundum naturam sint, fruentem vivere.

+ + + +

At iam decimum annum in spelunca iacet. Illa videamus, quae a te de amicitia dicta sunt. Hoc ipsum elegantius poni meliusque potuit. Apparet statim, quae sint officia, quae actiones. Hic ambiguo ludimur.

+ + + +

Quaesita enim virtus est, non quae relinqueret naturam, sed quae tueretur. Atque hoc loco similitudines eas, quibus illi uti solent, dissimillimas proferebas. Maximus dolor, inquit, brevis est. Quod cum dixissent, ille contra. Quis Aristidem non mortuum diligit? Cur tantas regiones barbarorum pedibus obiit, tot maria transmisit? Nummus in Croesi divitiis obscuratur, pars est tamen divitiarum.

+ + + +

Quae quo sunt excelsiores, eo dant clariora indicia naturae.

+ + + +

Beatus sibi videtur esse moriens. Aufert enim sensus actionemque tollit omnem. Certe non potest. Sed quae tandem ista ratio est? Istam voluptatem, inquit, Epicurus ignorat? Quis suae urbis conservatorem Codrum, quis Erechthei filias non maxime laudat? Universa enim illorum ratione cum tota vestra confligendum puto. Tum mihi Piso: Quid ergo?

+ + + +
    +
  • Et quod est munus, quod opus sapientiae?
  • + + + +
  • Quid, si non sensus modo ei sit datus, verum etiam animus hominis?
  • + + + +
  • Quid vero?
  • + + + +
  • Quamquam non negatis nos intellegere quid sit voluptas, sed quid ille dicat.
  • +
+ + + +
+

+ Inde sermone vario sex illa a Dipylo stadia confecimus. +

+
+ + + +
+

+ Ut necesse sit omnium rerum, quae natura vigeant, similem esse finem, non eundem. +

+
+ + + +
Idem iste, inquam, de voluptate quid sentit?
+ + + +

Tecum optime, deinde etiam cum mediocri amico. Nemo igitur esse beatus potest. At multis se probavit. Qui enim existimabit posse se miserum esse beatus non erit. Erat enim Polemonis. Eadem nunc mea adversum te oratio est. Inscite autem medicinae et gubernationis ultimum cum ultimo sapientiae comparatur. Hoc etsi multimodis reprehendi potest, tamen accipio, quod dant. Nemo igitur esse beatus potest.

+ + + +

Quae in controversiam veniunt, de iis, si placet, disseramus. Non enim in selectione virtus ponenda erat, ut id ipsum, quod erat bonorum ultimum, aliud aliquid adquireret. Sed quid attinet de rebus tam apertis plura requirere? Illud dico, ea, quae dicat, praeclare inter se cohaerere. Quae cum dixisset paulumque institisset, Quid est? At coluit ipse amicitias. Habent enim et bene longam et satis litigiosam disputationem. Quantam rem agas, ut Circeis qui habitet totum hunc mundum suum municipium esse existimet?

+ + + +

Quaesita enim virtus est, non quae relinqueret naturam, sed quae tueretur.

+ + + +

Quae cum ita sint, effectum est nihil esse malum, quod turpe non sit. Quod autem satis est, eo quicquid accessit, nimium est; At hoc in eo M. Cum id fugiunt, re eadem defendunt, quae Peripatetici, verba. Quid sequatur, quid repugnet, vident. Nulla profecto est, quin suam vim retineat a primo ad extremum. Ita ne hoc quidem modo paria peccata sunt. Nihil opus est exemplis hoc facere longius. Quod autem principium officii quaerunt, melius quam Pyrrho; Conferam tecum, quam cuique verso rem subicias;

+ + + +

Ostendit pedes et pectus. Minime vero, inquit ille, consentit. Idemne potest esse dies saepius, qui semel fuit? Duarum enim vitarum nobis erunt instituta capienda. Conclusum est enim contra Cyrenaicos satis acute, nihil ad Epicurum. Nos commodius agimus.

+ + + +

Cur igitur, inquam, res tam dissimiles eodem nomine appellas?

+ + + +

Sumenda potius quam expetenda. Ita graviter et severe voluptatem secrevit a bono. Atque hoc loco similitudines eas, quibus illi uti solent, dissimillimas proferebas. Quae quo sunt excelsiores, eo dant clariora indicia naturae. At iam decimum annum in spelunca iacet. Nam et a te perfici istam disputationem volo, nec tua mihi oratio longa videri potest. Somnum denique nobis, nisi requietem corporibus et is medicinam quandam laboris afferret, contra naturam putaremus datum; Ab hoc autem quaedam non melius quam veteres, quaedam omnino relicta. Quo modo?

+ + + +

Ratio quidem vestra sic cogit.

+ + + +

Philosophi autem in suis lectulis plerumque moriuntur. Nam Pyrrho, Aristo, Erillus iam diu abiecti. Haec igitur Epicuri non probo, inquam. In quo etsi est magnus, tamen nova pleraque et perpauca de moribus. Itaque eos id agere, ut a se dolores, morbos, debilitates repellant. Nunc haec primum fortasse audientis servire debemus. Sin autem est in ea, quod quidam volunt, nihil impedit hanc nostram comprehensionem summi boni. Non dolere, inquam, istud quam vim habeat postea videro; Ratio quidem vestra sic cogit.

+ + + +
Quodsi ipsam honestatem undique pertectam atque absolutam.
+ + + +

Ergo hoc quidem apparet, nos ad agendum esse natos. Tuo vero id quidem, inquam, arbitratu. Est autem officium, quod ita factum est, ut eius facti probabilis ratio reddi possit. Dicam, inquam, et quidem discendi causa magis, quam quo te aut Epicurum reprehensum velim. Cur deinde Metrodori liberos commendas?

+ + + +
    +
  • Se omnia, quae secundum naturam sint, b o n a appellare, quae autem contra, m a l a.
  • + + + +
  • Inscite autem medicinae et gubernationis ultimum cum ultimo sapientiae comparatur.
  • + + + +
  • Minime vero, inquit ille, consentit.
  • + + + +
  • Vulgo enim dicitur: Iucundi acti labores, nec male Euripidesconcludam, si potero, Latine;
  • + + + +
  • Universa enim illorum ratione cum tota vestra confligendum puto.
  • +
+ + + +
Ut proverbia non nulla veriora sint quam vestra dogmata.
+ + + +

Ita prorsus, inquam; Quam nemo umquam voluptatem appellavit, appellat; Minime vero, inquit ille, consentit. An vero, inquit, quisquam potest probare, quod perceptfum, quod. Nihilo beatiorem esse Metellum quam Regulum. Dolere malum est: in crucem qui agitur, beatus esse non potest. Nam adhuc, meo fortasse vitio, quid ego quaeram non perspicis. Inscite autem medicinae et gubernationis ultimum cum ultimo sapientiae comparatur. Nihil opus est exemplis hoc facere longius.

+ + + +
+

+ Sive hoc difficile est, tamen nec modus est ullus investigandi veri, nisi inveneris, et quaerendi defatigatio turpis est, cum id, quod quaeritur, sit pulcherrimum. +

+
+ + + +

Miserum hominem! Si dolor summum malum est, dici aliter non potest.

+ + + +

Itaque hic ipse iam pridem est reiectus; Et quidem, inquit, vehementer errat; Servari enim iustitia nisi a forti viro, nisi a sapiente non potest. Hunc vos beatum; Nam Pyrrho, Aristo, Erillus iam diu abiecti. Ex ea difficultate illae fallaciloquae, ut ait Accius, malitiae natae sunt. Vadem te ad mortem tyranno dabis pro amico, ut Pythagoreus ille Siculo fecit tyranno? Ego quoque, inquit, didicerim libentius si quid attuleris, quam te reprehenderim. Plane idem, inquit, et maxima quidem, qua fieri nulla maior potest.

+ + + +

Vide, ne etiam menses! nisi forte eum dicis, qui, simul atque arripuit, interficit. Age, inquies, ista parva sunt. Quae adhuc, Cato, a te dicta sunt, eadem, inquam, dicere posses, si sequerere Pyrrhonem aut Aristonem. Ratio enim nostra consentit, pugnat oratio. Immo alio genere; Nulla profecto est, quin suam vim retineat a primo ad extremum.

+ + + +
+ + + +

Mihi enim satis est, ipsis non satis. Bonum liberi: misera orbitas. Ita fit beatae vitae domina fortuna, quam Epicurus ait exiguam intervenire sapienti. Nosti, credo, illud: Nemo pius est, qui pietatem-; Quamquam id quidem, infinitum est in hac urbe; Id est enim, de quo quaerimus. Quoniam, si dis placet, ab Epicuro loqui discimus. Beatus sibi videtur esse moriens. Haec dicuntur inconstantissime.

+ + + +
    +
  • Negat enim summo bono afferre incrementum diem.
  • + + + +
  • Deinde prima illa, quae in congressu solemus: Quid tu, inquit, huc?
  • + + + +
  • Cuius similitudine perspecta in formarum specie ac dignitate transitum est ad honestatem dictorum atque factorum.
  • + + + +
  • Si enim non fuit eorum iudicii, nihilo magis hoc non addito illud est iudicatum-.
  • + + + +
  • Et hanc quidem primam exigam a te operam, ut audias me quae a te dicta sunt refellentem.
  • +
+ + + +
    +
  • At hoc in eo M.
  • + + + +
  • Indicant pueri, in quibus ut in speculis natura cernitur.
  • + + + +
  • In qua si nihil est praeter rationem, sit in una virtute finis bonorum;
  • + + + +
  • Hoc etsi multimodis reprehendi potest, tamen accipio, quod dant.
  • + + + +
  • Atque haec coniunctio confusioque virtutum tamen a philosophis ratione quadam distinguitur.
  • + + + +
  • Ex ea difficultate illae fallaciloquae, ut ait Accius, malitiae natae sunt.
  • +
+ + + +

Qui-vere falsone, quaerere mittimus-dicitur oculis se privasse; Audio equidem philosophi vocem, Epicure, sed quid tibi dicendum sit oblitus es. Illud dico, ea, quae dicat, praeclare inter se cohaerere. Apparet statim, quae sint officia, quae actiones. Haeret in salebra. Est igitur officium eius generis, quod nec in bonis ponatur nec in contrariis. Praeteritis, inquit, gaudeo.

+ + + +

Hic, qui utrumque probat, ambobus debuit uti, sicut facit re, neque tamen dividit verbis. Sed ego in hoc resisto; Si enim, ut mihi quidem videtur, non explet bona naturae voluptas, iure praetermissa est; Mihi quidem Homerus huius modi quiddam vidisse videatur in iis, quae de Sirenum cantibus finxerit. Quid sequatur, quid repugnet, vident. In schola desinis.

+ + + +

A primo, ut opinor, animantium ortu petitur origo summi boni. Re mihi non aeque satisfacit, et quidem locis pluribus. Qui-vere falsone, quaerere mittimus-dicitur oculis se privasse; Cum praesertim illa perdiscere ludus esset. Dic in quovis conventu te omnia facere, ne doleas. Te ipsum, dignissimum maioribus tuis, voluptasne induxit, ut adolescentulus eriperes P. Atque his de rebus et splendida est eorum et illustris oratio. Sed haec quidem liberius ab eo dicuntur et saepius.

+ + + +

Si enim ad populum me vocas, eum. Ita multo sanguine profuso in laetitia et in victoria est mortuus. Nec vero alia sunt quaerenda contra Carneadeam illam sententiam. Stoici autem, quod finem bonorum in una virtute ponunt, similes sunt illorum; Gloriosa ostentatio in constituendo summo bono. Dulce amarum, leve asperum, prope longe, stare movere, quadratum rotundum. Nunc de hominis summo bono quaeritur; Maximas vero virtutes iacere omnis necesse est voluptate dominante.

+ + + +

Ut aliquid scire se gaudeant? Aliter enim nosmet ipsos nosse non possumus.

+ + + +

Quae cum dixisset, finem ille. Haec para/doca illi, nos admirabilia dicamus.

+ + + +

Aliter enim nosmet ipsos nosse non possumus. Que Manilium, ab iisque M. Sed hoc sane concedamus. Cum id fugiunt, re eadem defendunt, quae Peripatetici, verba. Tria genera cupiditatum, naturales et necessariae, naturales et non necessariae, nec naturales nec necessariae. De quibus cupio scire quid sentias. Omnia contraria, quos etiam insanos esse vultis. Tum Piso: Quoniam igitur aliquid omnes, quid Lucius noster? Idem iste, inquam, de voluptate quid sentit? Cur id non ita fit?

+ + + +
+

+ Tantum dico, magis fuisse vestrum agere Epicuri diem natalem, quam illius testamento cavere ut ageretur. +

+
+ + + +
Atqui haec patefactio quasi rerum opertarum, cum quid quidque sit aperitur, definitio est.
+ + + +

In quibus doctissimi illi veteres inesse quiddam caeleste et divinum putaverunt. Non quaeritur autem quid naturae tuae consentaneum sit, sed quid disciplinae. Videmusne ut pueri ne verberibus quidem a contemplandis rebus perquirendisque deterreantur? Quid affers, cur Thorius, cur Caius Postumius, cur omnium horum magister, Orata, non iucundissime vixerit? Neutrum vero, inquit ille. Sextilio Rufo, cum is rem ad amicos ita deferret, se esse heredem Q. Inde sermone vario sex illa a Dipylo stadia confecimus. Respondent extrema primis, media utrisque, omnia omnibus. Varietates autem iniurasque fortunae facile veteres philosophorum praeceptis instituta vita superabat. Facillimum id quidem est, inquam. Qui est in parvis malis.

+ + + +

Igitur ne dolorem quidem.

+ + + +

An est aliquid, quod te sua sponte delectet? Tecum optime, deinde etiam cum mediocri amico.

+ + + +
    +
  • His similes sunt omnes, qui virtuti student levantur vitiis, levantur erroribus, nisi forte censes Ti.
  • + + + +
  • Ergo omni animali illud, quod appetiti positum est in eo, quod naturae est accommodatum.
  • + + + +
  • Igitur neque stultorum quisquam beatus neque sapientium non beatus.
  • + + + +
  • Tum ille: Ain tandem?
  • +
+ + + +

Bona autem corporis huic sunt, quod posterius posui, similiora. Itaque dicunt nec dubitant: mihi sic usus est, tibi ut opus est facto, fac. Vitae autem degendae ratio maxime quidem illis placuit quieta. Deinde dolorem quem maximum? Si mala non sunt, iacet omnis ratio Peripateticorum. Ita multa dicunt, quae vix intellegam. Quamquam haec quidem praeposita recte et reiecta dicere licebit. Non est ista, inquam, Piso, magna dissensio. Id enim volumus, id contendimus, ut officii fructus sit ipsum officium. Nihil opus est exemplis hoc facere longius. Sed ad bona praeterita redeamus. Itaque contra est, ac dicitis; Hanc quoque iucunditatem, si vis, transfer in animum;

+ + + +

Sequitur disserendi ratio cognitioque naturae; Zenonis est, inquam, hoc Stoici. Themistocles quidem, cum ei Simonides an quis alius artem memoriae polliceretur, Oblivionis, inquit, mallem. Videmusne ut pueri ne verberibus quidem a contemplandis rebus perquirendisque deterreantur? Aliis esse maiora, illud dubium, ad id, quod summum bonum dicitis, ecquaenam possit fieri accessio. Ut in geometria, prima si dederis, danda sunt omnia. Si enim ita est, vide ne facinus facias, cum mori suadeas. Inde sermone vario sex illa a Dipylo stadia confecimus. Expectoque quid ad id, quod quaerebam, respondeas. Hoc dictum in una re latissime patet, ut in omnibus factis re, non teste moveamur. Esse enim, nisi eris, non potes.

+ + + +

Non igitur de improbo, sed de callido improbo quaerimus, qualis Q. Qui ita affectus, beatum esse numquam probabis; Vitiosum est enim in dividendo partem in genere numerare. Suo enim quisque studio maxime ducitur. Quare conare, quaeso. Tu autem negas fortem esse quemquam posse, qui dolorem malum putet. Sin ea non neglegemus neque tamen ad finem summi boni referemus, non multum ab Erilli levitate aberrabimus.

+ + + +

Aliter enim explicari, quod quaeritur, non potest. Ita relinquet duas, de quibus etiam atque etiam consideret. Hunc vos beatum; Quis istum dolorem timet?

+ + + +

Non est ista, inquam, Piso, magna dissensio. Tubulo putas dicere? Teneo, inquit, finem illi videri nihil dolere. Summus dolor plures dies manere non potest? Octavio fuit, cum illam severitatem in eo filio adhibuit, quem in adoptionem D. Quae cum essent dicta, discessimus. Ac tamen hic mallet non dolere. Nam quibus rebus efficiuntur voluptates, eae non sunt in potestate sapientis. Ea possunt paria non esse. Illa sunt similia: hebes acies est cuipiam oculorum, corpore alius senescit;

+ + + +

Itaque eos id agere, ut a se dolores, morbos, debilitates repellant.

+ + + +

Nec tamen ullo modo summum pecudis bonum et hominis idem mihi videri potest. Negat esse eam, inquit, propter se expetendam. Nihilo beatiorem esse Metellum quam Regulum. Ut alios omittam, hunc appello, quem ille unum secutus est. Eaedem enim utilitates poterunt eas labefactare atque pervertere. Et harum quidem rerum facilis est et expedita distinctio. At iste non dolendi status non vocatur voluptas. Qui-vere falsone, quaerere mittimus-dicitur oculis se privasse; Nondum autem explanatum satis, erat, quid maxime natura vellet. Itaque eos id agere, ut a se dolores, morbos, debilitates repellant.

+ + + +

Quae contraria sunt his, malane? Paulum, cum regem Persem captum adduceret, eodem flumine invectio? Eam stabilem appellas. Nec vero alia sunt quaerenda contra Carneadeam illam sententiam. A mene tu? Uterque enim summo bono fruitur, id est voluptate. Nunc haec primum fortasse audientis servire debemus.

+ + + +
Ait enim se, si uratur, Quam hoc suave! dicturum.
+ + + +

Inquit, respondet: Quia, nisi quod honestum est, nullum est aliud bonum! Non quaero iam verumne sit; Quae cum dixisset, finem ille. Res enim se praeclare habebat, et quidem in utraque parte. Nosti, credo, illud: Nemo pius est, qui pietatem-; Nos cum te, M. Intrandum est igitur in rerum naturam et penitus quid ea postulet pervidendum;

+ + + +
+

+ Quid paulo ante, inquit, dixerim nonne meministi, cum omnis dolor detractus esset, variari, non augeri voluptatem? +

+
+ + + +

Cum salvum esse flentes sui respondissent, rogavit essentne fusi hostes.

+ + + +

At ille pellit, qui permulcet sensum voluptate. Sed haec quidem liberius ab eo dicuntur et saepius. Si sapiens, ne tum quidem miser, cum ab Oroete, praetore Darei, in crucem actus est. Praeclare hoc quidem.

+ + + +

Graecum enim hunc versum nostis omnes-: Suavis laborum est praeteritorum memoria. Ut in geometria, prima si dederis, danda sunt omnia. Nihil ad rem! Ne sit sane; Nunc omni virtuti vitium contrario nomine opponitur. Quid iudicant sensus? Age, inquies, ista parva sunt. Ab his oratores, ab his imperatores ac rerum publicarum principes extiterunt.

+ + + +

Moriatur, inquit. Bonum integritas corporis: misera debilitas. Haec igitur Epicuri non probo, inquam. Contemnit enim disserendi elegantiam, confuse loquitur. Age sane, inquam. Quamquam te quidem video minime esse deterritum. Respondent extrema primis, media utrisque, omnia omnibus. Istam voluptatem, inquit, Epicurus ignorat?

+ + + +
    +
  • Qui est in parvis malis.
  • + + + +
  • Sapientem locupletat ipsa natura, cuius divitias Epicurus parabiles esse docuit.
  • + + + +
  • Quid, si reviviscant Platonis illi et deinceps qui eorum auditores fuerunt, et tecum ita loquantur?
  • + + + +
  • Non est igitur summum malum dolor.
  • + + + +
  • In quo etsi est magnus, tamen nova pleraque et perpauca de moribus.
  • + + + +
  • Et ille ridens: Video, inquit, quid agas;
  • +
+ + + +

Quo plebiscito decreta a senatu est consuli quaestio Cn. Quae quidem vel cum periculo est quaerenda vobis;

+ + + +

Nam ista vestra: Si gravis, brevis; Si quae forte-possumus.

+ + + +

Sed haec quidem liberius ab eo dicuntur et saepius.

+ + + +

Sint modo partes vitae beatae. Iam doloris medicamenta illa Epicurea tamquam de narthecio proment: Si gravis, brevis; Inde sermone vario sex illa a Dipylo stadia confecimus. Inde igitur, inquit, ordiendum est. Velut ego nunc moveor. Ex quo illud efficitur, qui bene cenent omnis libenter cenare, qui libenter, non continuo bene.

+ + + +

Verum esto: verbum ipsum voluptatis non habet dignitatem, nec nos fortasse intellegimus. Huius, Lyco, oratione locuples, rebus ipsis ielunior. Quid igitur dubitamus in tota eius natura quaerere quid sit effectum? Nec tamen ullo modo summum pecudis bonum et hominis idem mihi videri potest. An potest, inquit ille, quicquam esse suavius quam nihil dolere? Ita ne hoc quidem modo paria peccata sunt. Quamquam tu hanc copiosiorem etiam soles dicere.

+ + + +

Qui enim voluptatem ipsam contemnunt, iis licet dicere se acupenserem maenae non anteponere. Quamquam tu hanc copiosiorem etiam soles dicere. Quamquam tu hanc copiosiorem etiam soles dicere. Ergo et avarus erit, sed finite, et adulter, verum habebit modum, et luxuriosus eodem modo. Et quidem iure fortasse, sed tamen non gravissimum est testimonium multitudinis. Non dolere, inquam, istud quam vim habeat postea videro; Quis istud possit, inquit, negare? Age, inquies, ista parva sunt. Incommoda autem et commoda-ita enim estmata et dustmata appello-communia esse voluerunt, paria noluerunt. Si de re disceptari oportet, nulla mihi tecum, Cato, potest esse dissensio.

+ + + +
+

+ Ita, quae mutat, ea corrumpit, quae sequitur sunt tota Democriti, atomi, inane, imagines, quae eidola nominant, quorum incursione non solum videamus, sed etiam cogitemus; +

+
+ + + +
Sin laboramus, quis est, qui alienae modum statuat industriae?
+ + + +

Quid enim possumus hoc agere divinius? Sapiens autem semper beatus est et est aliquando in dolore; Memini vero, inquam; In his igitur partibus duabus nihil erat, quod Zeno commutare gestiret. Mihi quidem Antiochum, quem audis, satis belle videris attendere. Vitiosum est enim in dividendo partem in genere numerare.

+ + + +

Quis est enim, in quo sit cupiditas, quin recte cupidus dici possit? Haec bene dicuntur, nec ego repugno, sed inter sese ipsa pugnant. Illis videtur, qui illud non dubitant bonum dicere -; At certe gravius.

+ + + +
Invidiosum nomen est, infame, suspectum.
+ + + +

Dicet pro me ipsa virtus nec dubitabit isti vestro beato M. Eiuro, inquit adridens, iniquum, hac quidem de re; Quis non odit sordidos, vanos, leves, futtiles? Equidem e Cn.

+ + + +
    +
  • Quem Tiberina descensio festo illo die tanto gaudio affecit, quanto L.
  • + + + +
  • Ergo ita: non posse honeste vivi, nisi honeste vivatur?
  • + + + +
  • Quodcumque in mentem incideret, et quodcumque tamquam occurreret.
  • + + + +
  • Itaque primos congressus copulationesque et consuetudinum instituendarum voluntates fieri propter voluptatem;
  • + + + +
  • Virtutibus igitur rectissime mihi videris et ad consuetudinem nostrae orationis vitia posuisse contraria.
  • + + + +
  • Aliter enim explicari, quod quaeritur, non potest.
  • +
+ + + +

Hic ambiguo ludimur.

+ + + +

Aliter enim nosmet ipsos nosse non possumus. Oculorum, inquit Plato, est in nobis sensus acerrimus, quibus sapientiam non cernimus. Animadverti, ínquam, te isto modo paulo ante ponere, et scio ab Antiocho nostro dici sic solere; Si quicquam extra virtutem habeatur in bonis.

+ + + +

Tria genera bonorum; Nihilne te delectat umquam -video, quicum loquar-, te igitur, Torquate, ipsum per se nihil delectat? Ne in odium veniam, si amicum destitero tueri. Equidem e Cn. Et hunc idem dico, inquieta sed ad virtutes et ad vitia nihil interesse. An eum discere ea mavis, quae cum plane perdidiceriti nihil sciat? Nam adhuc, meo fortasse vitio, quid ego quaeram non perspicis. Age, inquies, ista parva sunt.

+ + + +

Non ego tecum iam ita iocabor, ut isdem his de rebus, cum L.

+ + + +

Sic consequentibus vestris sublatis prima tolluntur. Suo genere perveniant ad extremum; Vitiosum est enim in dividendo partem in genere numerare. Rationis enim perfectio est virtus; Putabam equidem satis, inquit, me dixisse. Quae similitudo in genere etiam humano apparet.

+ + + +

Beatus autem esse in maximarum rerum timore nemo potest. Quo plebiscito decreta a senatu est consuli quaestio Cn. Qui autem de summo bono dissentit de tota philosophiae ratione dissentit. Duo enim genera quae erant, fecit tria. Qui-vere falsone, quaerere mittimus-dicitur oculis se privasse; Non autem hoc: igitur ne illud quidem.

+ + + +

Sed fac ista esse non inportuna; Omnia contraria, quos etiam insanos esse vultis. Videamus animi partes, quarum est conspectus illustrior; Atque haec ita iustitiae propria sunt, ut sint virtutum reliquarum communia. Quid enim necesse est, tamquam meretricem in matronarum coetum, sic voluptatem in virtutum concilium adducere? Eaedem res maneant alio modo. Cur deinde Metrodori liberos commendas? Quid ergo? Ut alios omittam, hunc appello, quem ille unum secutus est. Scripta sane et multa et polita, sed nescio quo pacto auctoritatem oratio non habet. An est aliquid, quod te sua sponte delectet? Sed hoc sane concedamus.

+ + + +

Intellegi quidem, ut propter aliam quampiam rem, verbi gratia propter voluptatem, nos amemus;

+ + + +

An dolor longissimus quisque miserrimus, voluptatem non optabiliorem diuturnitas facit? Ergo ita: non posse honeste vivi, nisi honeste vivatur? Teneo, inquit, finem illi videri nihil dolere. Nunc omni virtuti vitium contrario nomine opponitur. Quorum sine causa fieri nihil putandum est. Iam id ipsum absurdum, maximum malum neglegi. Nos paucis ad haec additis finem faciamus aliquando; Quod mihi quidem visus est, cum sciret, velle tamen confitentem audire Torquatum.

+ + + +

Tu enim ista lenius, hic Stoicorum more nos vexat. Quid enim tanto opus est instrumento in optimis artibus comparandis? Bonum integritas corporis: misera debilitas. At modo dixeras nihil in istis rebus esse, quod interesset. Habes, inquam, Cato, formam eorum, de quibus loquor, philosophorum.

+ + + +

Efficiens dici potest.

+ + + +

Isto modo ne improbos quidem, si essent boni viri. An est aliquid per se ipsum flagitiosum, etiamsi nulla comitetur infamia? Satisne vobis videor pro meo iure in vestris auribus commentatus? Frater et T. Sed vos squalidius, illorum vides quam niteat oratio.

+ + + +
Eam tum adesse, cum dolor omnis absit;
+ + + +

Sit hoc ultimum bonorum, quod nunc a me defenditur; Animum autem reliquis rebus ita perfecit, ut corpus; Quae sequuntur igitur? Ut optime, secundum naturam affectum esse possit. Non potes, nisi retexueris illa. In quo etsi est magnus, tamen nova pleraque et perpauca de moribus. Occultum facinus esse potuerit, gaudebit;

+ + + +

Illa tamen simplicia, vestra versuta.

+ + + +

Sed quid attinet de rebus tam apertis plura requirere? Sin tantum modo ad indicia veteris memoriae cognoscenda, curiosorum. Bonum patria: miserum exilium. Atqui haec patefactio quasi rerum opertarum, cum quid quidque sit aperitur, definitio est. Quid, cum volumus nomina eorum, qui quid gesserint, nota nobis esse, parentes, patriam, multa praeterea minime necessaria? Quem si tenueris, non modo meum Ciceronem, sed etiam me ipsum abducas licebit.

+ + + +
+

+ Hic si Peripateticus fuisset, permansisset, credo, in sententia, qui dolorem malum dicunt esse, de asperitate autem eius fortiter ferenda praecipiunt eadem, quae Stoici. +

+
+ + + +
+

+ Equidem etiam curiam nostram-Hostiliam dico, non hanc novam, quae minor mihi esse videtur, posteaquam est maior-solebam intuens Scipionem, Catonem, Laelium, nostrum vero in primis avum cogitare; +

+
+ + + +

Themistocles quidem, cum ei Simonides an quis alius artem memoriae polliceretur, Oblivionis, inquit, mallem. Nobis Heracleotes ille Dionysius flagitiose descivisse videtur a Stoicis propter oculorum dolorem. Theophrasti igitur, inquit, tibi liber ille placet de beata vita? Vide, ne etiam menses! nisi forte eum dicis, qui, simul atque arripuit, interficit. Tuo vero id quidem, inquam, arbitratu. Deprehensus omnem poenam contemnet. Age nunc isti doceant, vel tu potius quis enim ista melius? Cur igitur easdem res, inquam, Peripateticis dicentibus verbum nullum est, quod non intellegatur?

+ + + +

Bestiarum vero nullum iudicium puto.

+ + + +

Vos autem cum perspicuis dubia debeatis illustrare, dubiis perspicua conamini tollere. Qui ita affectus, beatum esse numquam probabis; Haec dicuntur inconstantissime. Primum Theophrasti, Strato, physicum se voluit; Satis est tibi in te, satis in legibus, satis in mediocribus amicitiis praesidii. Omnia peccata paria dicitis.

+ + + +
+

+ Ut ad minora veniam, mathematici, poëtae, musici, medici denique ex hac tamquam omnium artificum officina profecti sunt. +

+
+ + + +
    +
  • Huic mori optimum esse propter desperationem sapientiae, illi propter spem vivere.
  • + + + +
  • Ne in odium veniam, si amicum destitero tueri.
  • + + + +
  • Progredientibus autem aetatibus sensim tardeve potius quasi nosmet ipsos cognoscimus.
  • + + + +
  • Sed emolumenta communia esse dicuntur, recte autem facta et peccata non habentur communia.
  • +
+ + + +

Quo tandem modo?

+ + + +

Quia dolori non voluptas contraria est, sed doloris privatio. Consequens enim est et post oritur, ut dixi. Sed plane dicit quod intellegit. Et quod est munus, quod opus sapientiae? Nam quibus rebus efficiuntur voluptates, eae non sunt in potestate sapientis. Primum in nostrane potestate est, quid meminerimus?

+ + + +
Quid enim tanto opus est instrumento in optimis artibus comparandis?
+ + + +

Aliter enim explicari, quod quaeritur, non potest. At iam decimum annum in spelunca iacet. At eum nihili facit; Quamquam id quidem, infinitum est in hac urbe; Ne tum quidem te respicies et cogitabis sibi quemque natum esse et suis voluptatibus? Quid ergo aliud intellegetur nisi uti ne quae pars naturae neglegatur? Quid, quod res alia tota est?

+ + + +
Itaque his sapiens semper vacabit.
+ + + +
+ + + +

Isto modo ne improbos quidem, si essent boni viri. Nescio quo modo praetervolavit oratio. Nihil enim iam habes, quod ad corpus referas; Serpere anguiculos, nare anaticulas, evolare merulas, cornibus uti videmus boves, nepas aculeis. Quod cum dixissent, ille contra. Fortasse id optimum, sed ubi illud: Plus semper voluptatis? Si quidem, inquit, tollerem, sed relinquo. -, sed ut hoc iudicaremus, non esse in iis partem maximam positam beate aut secus vivendi. Aufert enim sensus actionemque tollit omnem. Itaque his sapiens semper vacabit.

+ + + +
    +
  • Sit, inquam, tam facilis, quam vultis, comparatio voluptatis, quid de dolore dicemus?
  • + + + +
  • Progredientibus autem aetatibus sensim tardeve potius quasi nosmet ipsos cognoscimus.
  • +
+ + + +

Numquam facies. Sed haec quidem liberius ab eo dicuntur et saepius. Quid, quod homines infima fortuna, nulla spe rerum gerendarum, opifices denique delectantur historia? Quod, inquit, quamquam voluptatibus quibusdam est saepe iucundius, tamen expetitur propter voluptatem. Verum hoc idem saepe faciamus. Cur igitur, inquam, res tam dissimiles eodem nomine appellas? Nos cum te, M. Ut in geometria, prima si dederis, danda sunt omnia.

+ + + +

Tuo vero id quidem, inquam, arbitratu. Apud ceteros autem philosophos, qui quaesivit aliquid, tacet; A mene tu? Istam voluptatem perpetuam quis potest praestare sapienti? Qui enim existimabit posse se miserum esse beatus non erit. Potius ergo illa dicantur: turpe esse, viri non esse debilitari dolore, frangi, succumbere. Quam nemo umquam voluptatem appellavit, appellat; Atque haec coniunctio confusioque virtutum tamen a philosophis ratione quadam distinguitur.

+ + + +
Quam illa ardentis amores excitaret sui! Cur tandem?
+ + + +

In motu et in statu corporis nihil inest, quod animadvertendum esse ipsa natura iudicet? Quod non faceret, si in voluptate summum bonum poneret. Quod, inquit, quamquam voluptatibus quibusdam est saepe iucundius, tamen expetitur propter voluptatem. At enim hic etiam dolore. Quarum ambarum rerum cum medicinam pollicetur, luxuriae licentiam pollicetur. Ea possunt paria non esse.

+ + + +

Tu vero, inquam, ducas licet, si sequetur; Eadem fortitudinis ratio reperietur. Atque ego: Scis me, inquam, istud idem sentire, Piso, sed a te opportune facta mentio est. In his igitur partibus duabus nihil erat, quod Zeno commutare gestiret. Qui-vere falsone, quaerere mittimus-dicitur oculis se privasse; Tamen a proposito, inquam, aberramus. Primum Theophrasti, Strato, physicum se voluit; Nihil enim iam habes, quod ad corpus referas; Quae contraria sunt his, malane?

+ + + +

Eam tum adesse, cum dolor omnis absit;

+ + + +

Quamquam haec quidem praeposita recte et reiecta dicere licebit. Bonum incolumis acies: misera caecitas. Urgent tamen et nihil remittunt. Non enim quaero quid verum, sed quid cuique dicendum sit. Nam, ut sint illa vendibiliora, haec uberiora certe sunt. Age nunc isti doceant, vel tu potius quis enim ista melius? Satis est ad hoc responsum. Comprehensum, quod cognitum non habet? Ergo in gubernando nihil, in officio plurimum interest, quo in genere peccetur. Haec dicuntur inconstantissime.

+ + + +

Ecce aliud simile dissimile. Quasi vero, inquit, perpetua oratio rhetorum solum, non etiam philosophorum sit. Itaque his sapiens semper vacabit. In schola desinis. Quo tandem modo? Potius inflammat, ut coercendi magis quam dedocendi esse videantur. Et nemo nimium beatus est; Sed quid attinet de rebus tam apertis plura requirere?

+ + + +

Verum hoc idem saepe faciamus. Idem iste, inquam, de voluptate quid sentit? Propter nos enim illam, non propter eam nosmet ipsos diligimus. Si de re disceptari oportet, nulla mihi tecum, Cato, potest esse dissensio. Istam voluptatem perpetuam quis potest praestare sapienti? Quae quo sunt excelsiores, eo dant clariora indicia naturae.

+ + + +
    +
  • Hic nihil fuit, quod quaereremus.
  • + + + +
  • Quid ergo attinet dicere: Nihil haberem, quod reprehenderem, si finitas cupiditates haberent?
  • + + + +
  • Sin kakan malitiam dixisses, ad aliud nos unum certum vitium consuetudo Latina traduceret.
  • +
+ + + +

Expectoque quid ad id, quod quaerebam, respondeas. Sin aliud quid voles, postea.

+ + + +
    +
  • At Zeno eum non beatum modo, sed etiam divitem dicere ausus est.
  • + + + +
  • Nihil opus est exemplis hoc facere longius.
  • + + + +
  • Minime vero istorum quidem, inquit.
  • + + + +
  • Experiamur igitur, inquit, etsi habet haec Stoicorum ratio difficilius quiddam et obscurius.
  • +
+ + + +

Atqui eorum nihil est eius generis, ut sit in fine atque extrerno bonorum. Mihi enim satis est, ipsis non satis. At tu eadem ista dic in iudicio aut, si coronam times, dic in senatu. Quis enim est, qui non videat haec esse in natura rerum tria? In qua quid est boni praeter summam voluptatem, et eam sempiternam?

+ + + +

Sin aliud quid voles, postea.

+ + + +

Inquit, dasne adolescenti veniam? Iam in altera philosophiae parte. Aliud igitur esse censet gaudere, aliud non dolere. Maximus dolor, inquit, brevis est. Summum a vobis bonum voluptas dicitur.

+ + + +

Tum Torquatus: Prorsus, inquit, assentior; Ut pulsi recurrant? Videmusne ut pueri ne verberibus quidem a contemplandis rebus perquirendisque deterreantur?

+ + + +

Ut nemo dubitet, eorum omnia officia quo spectare, quid sequi, quid fugere debeant? Philosophi autem in suis lectulis plerumque moriuntur. Videmus igitur ut conquiescere ne infantes quidem possint. Naturales divitias dixit parabiles esse, quod parvo esset natura contenta. Videsne quam sit magna dissensio? Sed plane dicit quod intellegit. Ait enim se, si uratur, Quam hoc suave! dicturum. Quia, si mala sunt, is, qui erit in iis, beatus non erit. Traditur, inquit, ab Epicuro ratio neglegendi doloris. Sunt autem, qui dicant foedus esse quoddam sapientium, ut ne minus amicos quam se ipsos diligant.

+ + + +
    +
  • Nobis Heracleotes ille Dionysius flagitiose descivisse videtur a Stoicis propter oculorum dolorem.
  • + + + +
  • In quibus doctissimi illi veteres inesse quiddam caeleste et divinum putaverunt.
  • + + + +
  • Ita multo sanguine profuso in laetitia et in victoria est mortuus.
  • +
+ + + +

Sic consequentibus vestris sublatis prima tolluntur.

+ + + +

Hoc tu nunc in illo probas. Illa videamus, quae a te de amicitia dicta sunt. Sed quoniam et advesperascit et mihi ad villam revertendum est, nunc quidem hactenus; Hunc vos beatum; Nos commodius agimus. Quasi ego id curem, quid ille aiat aut neget. Sic exclusis sententiis reliquorum cum praeterea nulla esse possit, haec antiquorum valeat necesse est.

+ + + +
+ + + +

Quo modo autem optimum, si bonum praeterea nullum est? Idemne, quod iucunde? Pauca mutat vel plura sane; Graece donan, Latine voluptatem vocant. Venit enim mihi Platonis in mentem, quem accepimus primum hic disputare solitum; Eorum enim omnium multa praetermittentium, dum eligant aliquid, quod sequantur, quasi curta sententia; Hi curatione adhibita levantur in dies, valet alter plus cotidie, alter videt.

+ + + +

In quibus doctissimi illi veteres inesse quiddam caeleste et divinum putaverunt.

+ + + +

O magnam vim ingenii causamque iustam, cur nova existeret disciplina! Perge porro. Non autem hoc: igitur ne illud quidem. Neque solum ea communia, verum etiam paria esse dixerunt. Ut in geometria, prima si dederis, danda sunt omnia.

+ + + +

Hoc non est positum in nostra actione.

+ + + +

Ac tamen hic mallet non dolere. Ergo et avarus erit, sed finite, et adulter, verum habebit modum, et luxuriosus eodem modo. Hoc enim constituto in philosophia constituta sunt omnia. Cur id non ita fit? Quid enim mihi potest esse optatius quam cum Catone, omnium virtutum auctore, de virtutibus disputare? Tertium autem omnibus aut maximis rebus iis, quae secundum naturam sint, fruentem vivere.

+ + + +

Animum autem reliquis rebus ita perfecit, ut corpus; Equidem e Cn. Sed utrum hortandus es nobis, Luci, inquit, an etiam tua sponte propensus es? Mihi, inquam, qui te id ipsum rogavi? Quae qui non vident, nihil umquam magnum ac cognitione dignum amaverunt. Qua ex cognitione facilior facta est investigatio rerum occultissimarum. Ac tamen hic mallet non dolere. Aliter enim nosmet ipsos nosse non possumus.

+ + + +
Cupit enim dícere nihil posse ad beatam vitam deesse sapienti.
+ + + +

Sed residamus, inquit, si placet. Nemo igitur esse beatus potest. Nam Pyrrho, Aristo, Erillus iam diu abiecti. Quippe: habes enim a rhetoribus; Respondeat totidem verbis. Quae est igitur causa istarum angustiarum? Tum ille: Ain tandem? Venit ad extremum; Et non ex maxima parte de tota iudicabis? Expectoque quid ad id, quod quaerebam, respondeas.

+ + + +
+

+ Itaque illa non dico me expetere, sed legere, nec optare, sed sumere, contraria autem non fugere, sed quasi secernere. +

+
+ + + +
    +
  • Sed haec ab Antiocho, familiari nostro, dicuntur multo melius et fortius, quam a Stasea dicebantur.
  • + + + +
  • Tamen aberramus a proposito, et, ne longius, prorsus, inquam, Piso, si ista mala sunt, placet.
  • + + + +
  • Invidiosum nomen est, infame, suspectum.
  • +
+ + + +

Quodsi ipsam honestatem undique pertectam atque absolutam.

+ + + +

Huius ego nunc auctoritatem sequens idem faciam. Itaque eos id agere, ut a se dolores, morbos, debilitates repellant. Praeclare enim Plato: Beatum, cui etiam in senectute contigerit, ut sapientiam verasque opiniones assequi possit. Solum praeterea formosum, solum liberum, solum civem, stultost; Quis est enim, in quo sit cupiditas, quin recte cupidus dici possit? Cur ipse Pythagoras et Aegyptum lustravit et Persarum magos adiit? Gracchum patrem non beatiorem fuisse quam fillum, cum alter stabilire rem publicam studuerit, alter evertere.

+ + + +
    +
  • Perturbationes autem nulla naturae vi commoventur, omniaque ea sunt opiniones ac iudicia levitatis.
  • + + + +
  • Ne in odium veniam, si amicum destitero tueri.
  • + + + +
  • Ita redarguitur ipse a sese, convincunturque scripta eius probitate ipsius ac moribus.
  • + + + +
  • Non minor, inquit, voluptas percipitur ex vilissimis rebus quam ex pretiosissimis.
  • +
+ + + +
    +
  • Quid, quod homines infima fortuna, nulla spe rerum gerendarum, opifices denique delectantur historia?
  • + + + +
  • At habetur! Et ego id scilicet nesciebam! Sed ut sit, etiamne post mortem coletur?
  • + + + +
  • Introduci enim virtus nullo modo potest, nisi omnia, quae leget quaeque reiciet, unam referentur ad summam.
  • + + + +
  • Nulla profecto est, quin suam vim retineat a primo ad extremum.
  • + + + +
  • Haec quo modo conveniant, non sane intellego.
  • + + + +
  • Multa sunt dicta ab antiquis de contemnendis ac despiciendis rebus humanis;
  • +
+ + + +

Sed tamen intellego quid velit.

+ + + +

Eiuro, inquit adridens, iniquum, hac quidem de re; Tollenda est atque extrahenda radicitus. Stoici autem, quod finem bonorum in una virtute ponunt, similes sunt illorum; Diodorus, eius auditor, adiungit ad honestatem vacuitatem doloris. Est autem a te semper dictum nec gaudere quemquam nisi propter corpus nec dolere. Respondent extrema primis, media utrisque, omnia omnibus. Haec bene dicuntur, nec ego repugno, sed inter sese ipsa pugnant. Nunc ita separantur, ut disiuncta sint, quo nihil potest esse perversius. Quamquam te quidem video minime esse deterritum. Ad eas enim res ab Epicuro praecepta dantur.

+ + + +
Traditur, inquit, ab Epicuro ratio neglegendi doloris.
+ + + +

Et ille ridens: Video, inquit, quid agas; Est autem etiam actio quaedam corporis, quae motus et status naturae congruentis tenet; Sumenda potius quam expetenda. Quam ob rem tandem, inquit, non satisfacit? Quid est enim aliud esse versutum? Sin laboramus, quis est, qui alienae modum statuat industriae?

+ + + +

Si de re disceptari oportet, nulla mihi tecum, Cato, potest esse dissensio. Nam quibus rebus efficiuntur voluptates, eae non sunt in potestate sapientis. Quo modo autem optimum, si bonum praeterea nullum est? Idcirco enim non desideraret, quia, quod dolore caret, id in voluptate est.

+ + + +
    +
  • Varietates autem iniurasque fortunae facile veteres philosophorum praeceptis instituta vita superabat.
  • + + + +
  • Qui autem de summo bono dissentit de tota philosophiae ratione dissentit.
  • + + + +
  • Iam contemni non poteris.
  • + + + +
  • Quae diligentissime contra Aristonem dicuntur a Chryippo.
  • +
+ + + +
Venit ad extremum;
+ + + +

Multoque hoc melius nos veriusque quam Stoici. Ergo in gubernando nihil, in officio plurimum interest, quo in genere peccetur. Praeterea sublata cognitione et scientia tollitur omnis ratio et vitae degendae et rerum gerendarum. Ita relinquet duas, de quibus etiam atque etiam consideret. Tecum optime, deinde etiam cum mediocri amico. Egone quaeris, inquit, quid sentiam? Haec dicuntur fortasse ieiunius; Quae cum dixisset paulumque institisset, Quid est? Quis est, qui non oderit libidinosam, protervam adolescentiam?

+ + + +

Et quidem saepe quaerimus verbum Latinum par Graeco et quod idem valeat; An vero, inquit, quisquam potest probare, quod perceptfum, quod. Octavio fuit, cum illam severitatem in eo filio adhibuit, quem in adoptionem D. Itaque his sapiens semper vacabit. Quod non faceret, si in voluptate summum bonum poneret. Equidem etiam Epicurum, in physicis quidem, Democriteum puto. Prodest, inquit, mihi eo esse animo. Cur iustitia laudatur?

+ + + +

Tum ille: Tu autem cum ipse tantum librorum habeas, quos hic tandem requiris? Tu vero, inquam, ducas licet, si sequetur; Quarum ambarum rerum cum medicinam pollicetur, luxuriae licentiam pollicetur. Nemo nostrum istius generis asotos iucunde putat vivere. Ergo, inquit, tibi Q. Ut optime, secundum naturam affectum esse possit. Hoc est dicere: Non reprehenderem asotos, si non essent asoti. Nam quibus rebus efficiuntur voluptates, eae non sunt in potestate sapientis. Certe, nisi voluptatem tanti aestimaretis. Inscite autem medicinae et gubernationis ultimum cum ultimo sapientiae comparatur.

+ + + +

Ergo, si semel tristior effectus est, hilara vita amissa est? Itaque mihi non satis videmini considerare quod iter sit naturae quaeque progressio. Qualem igitur hominem natura inchoavit? Ratio quidem vestra sic cogit. Sed ad illum redeo. Quicquid porro animo cernimus, id omne oritur a sensibus; Nunc omni virtuti vitium contrario nomine opponitur. Videmus igitur ut conquiescere ne infantes quidem possint. Quod idem cum vestri faciant, non satis magnam tribuunt inventoribus gratiam.

+ + + +

Idem iste, inquam, de voluptate quid sentit? Quis hoc dicit? Pisone in eo gymnasio, quod Ptolomaeum vocatur, unaque nobiscum Q. An potest cupiditas finiri? Dic in quovis conventu te omnia facere, ne doleas. Est, ut dicis, inquit; Traditur, inquit, ab Epicuro ratio neglegendi doloris. Et nemo nimium beatus est;

+ + + +

Satis est ad hoc responsum. Nec tamen ullo modo summum pecudis bonum et hominis idem mihi videri potest. Haec igitur Epicuri non probo, inquam. Pollicetur certe.

+ + + +
    +
  • Et quidem Arcesilas tuus, etsi fuit in disserendo pertinacior, tamen noster fuit;
  • + + + +
  • Quo studio cum satiari non possint, omnium ceterarum rerum obliti níhil abiectum, nihil humile cogitant;
  • + + + +
  • Haec quo modo conveniant, non sane intellego.
  • + + + +
  • Is ita vivebat, ut nulla tam exquisita posset inveniri voluptas, qua non abundaret.
  • + + + +
  • Stulti autem malorum memoria torquentur, sapientes bona praeterita grata recordatione renovata delectant.
  • +
+ + + +
    +
  • Neminem videbis ita laudatum, ut artifex callidus comparandarum voluptatum diceretur.
  • + + + +
  • Mihi enim erit isdem istis fortasse iam utendum.
  • + + + +
  • Quamquam ab iis philosophiam et omnes ingenuas disciplinas habemus;
  • + + + +
  • At enim sequor utilitatem.
  • + + + +
  • Praetereo multos, in bis doctum hominem et suavem, Hieronymum, quem iam cur Peripateticum appellem nescio.
  • +
+ + + +
Ipse Epicurus fortasse redderet, ut Sextus Peducaeus, Sex.
+ + + +

Itaque eos id agere, ut a se dolores, morbos, debilitates repellant. Non enim, si omnia non sequebatur, idcirco non erat ortus illinc. Nam quid possumus facere melius? Suo genere perveniant ad extremum; Hoc est non modo cor non habere, sed ne palatum quidem. Superiores tres erant, quae esse possent, quarum est una sola defensa, eaque vehementer. Illis videtur, qui illud non dubitant bonum dicere -; An me, inquis, tam amentem putas, ut apud imperitos isto modo loquar? Qui autem de summo bono dissentit de tota philosophiae ratione dissentit. Praeterea sublata cognitione et scientia tollitur omnis ratio et vitae degendae et rerum gerendarum. Quippe: habes enim a rhetoribus; Ita fit cum gravior, tum etiam splendidior oratio.

+ + + +

Haec igitur Epicuri non probo, inquam. Quorum altera prosunt, nocent altera. An vero, inquit, quisquam potest probare, quod perceptfum, quod. Ita enim vivunt quidam, ut eorum vita refellatur oratio. Ita multo sanguine profuso in laetitia et in victoria est mortuus.

+ + + +

Satis est ad hoc responsum.

+ + + +

Peccata paria. Scrupulum, inquam, abeunti; Semovenda est igitur voluptas, non solum ut recta sequamini, sed etiam ut loqui deceat frugaliter. Idemne, quod iucunde? Bonum liberi: misera orbitas. Tecum optime, deinde etiam cum mediocri amico.

+ + + +

At enim hic etiam dolore. Graece donan, Latine voluptatem vocant. Iam in altera philosophiae parte. Istam voluptatem, inquit, Epicurus ignorat? Nonne igitur tibi videntur, inquit, mala? Haec dicuntur inconstantissime. Dic in quovis conventu te omnia facere, ne doleas.

+ + + +

An tu me de L.

+ + + +

Itaque hic ipse iam pridem est reiectus; Nummus in Croesi divitiis obscuratur, pars est tamen divitiarum. Sed non alienum est, quo facilius vis verbi intellegatur, rationem huius verbi faciendi Zenonis exponere. Nihil enim iam habes, quod ad corpus referas; Quamvis enim depravatae non sint, pravae tamen esse possunt. Cur tantas regiones barbarorum pedibus obiit, tot maria transmisit? Nunc agendum est subtilius. Optime, inquam.

+ + + +

His similes sunt omnes, qui virtuti student levantur vitiis, levantur erroribus, nisi forte censes Ti. Est autem etiam actio quaedam corporis, quae motus et status naturae congruentis tenet; Incommoda autem et commoda-ita enim estmata et dustmata appello-communia esse voluerunt, paria noluerunt. Ita relinquet duas, de quibus etiam atque etiam consideret. Si sapiens, ne tum quidem miser, cum ab Oroete, praetore Darei, in crucem actus est. Quae autem natura suae primae institutionis oblita est? Quid, si etiam iucunda memoria est praeteritorum malorum? An ea, quae per vinitorem antea consequebatur, per se ipsa curabit? Sit sane ista voluptas. Aliter homines, aliter philosophos loqui putas oportere?

+ + + +

Itaque contra est, ac dicitis; Dicam, inquam, et quidem discendi causa magis, quam quo te aut Epicurum reprehensum velim. Earum etiam rerum, quas terra gignit, educatio quaedam et perfectio est non dissimilis animantium. Sin tantum modo ad indicia veteris memoriae cognoscenda, curiosorum. Dici enim nihil potest verius. Hoc positum in Phaedro a Platone probavit Epicurus sensitque in omni disputatione id fieri oportere. Praeterea sublata cognitione et scientia tollitur omnis ratio et vitae degendae et rerum gerendarum. Tu quidem reddes; Aliter enim nosmet ipsos nosse non possumus.

+ + + +

Nescio quo modo praetervolavit oratio. Servari enim iustitia nisi a forti viro, nisi a sapiente non potest. Quod cum ita sit, perspicuum est omnis rectas res atque laudabilis eo referri, ut cum voluptate vivatur. Zenonis est, inquam, hoc Stoici. Nunc haec primum fortasse audientis servire debemus. At iste non dolendi status non vocatur voluptas. Ne discipulum abducam, times.

+ + + +

Habes, inquam, Cato, formam eorum, de quibus loquor, philosophorum. Istam voluptatem perpetuam quis potest praestare sapienti? Ut optime, secundum naturam affectum esse possit. Audeo dicere, inquit. Ut optime, secundum naturam affectum esse possit. Id mihi magnum videtur. A villa enim, credo, et: Si ibi te esse scissem, ad te ipse venissem. Sed tu istuc dixti bene Latine, parum plane. Cum autem in quo sapienter dicimus, id a primo rectissime dicitur. Equidem e Cn. Sed tamen est aliquid, quod nobis non liceat, liceat illis.

+ + + +
Sed potestne rerum maior esse dissensio?
+ + + +

Inde sermone vario sex illa a Dipylo stadia confecimus. Hinc ceteri particulas arripere conati suam quisque videro voluit afferre sententiam. Cupit enim dícere nihil posse ad beatam vitam deesse sapienti. Respondent extrema primis, media utrisque, omnia omnibus. Eam tum adesse, cum dolor omnis absit; Ait enim se, si uratur, Quam hoc suave! dicturum. Te enim iudicem aequum puto, modo quae dicat ille bene noris. Quid censes in Latino fore? Nam, ut sint illa vendibiliora, haec uberiora certe sunt.

+ + + +

Quonam, inquit, modo?

+ + + +

Quae cum dixisset paulumque institisset, Quid est? Sed fortuna fortis; Tubulo putas dicere? Non enim, si omnia non sequebatur, idcirco non erat ortus illinc. Sequitur disserendi ratio cognitioque naturae; Quaesita enim virtus est, non quae relinqueret naturam, sed quae tueretur.

+ + + +
+

+ At vero Epicurus una in domo, et ea quidem angusta, quam magnos quantaque amoris conspiratione consentientis tenuit amicorum greges! quod fit etiam nunc ab Epicureis. +

+
+ + + +

Inde igitur, inquit, ordiendum est. At hoc in eo M. Multoque hoc melius nos veriusque quam Stoici. Ex quo, id quod omnes expetunt, beate vivendi ratio inveniri et comparari potest. Teneo, inquit, finem illi videri nihil dolere. Minime vero istorum quidem, inquit. Ex eorum enim scriptis et institutis cum omnis doctrina liberalis, omnis historia. Aliud igitur esse censet gaudere, aliud non dolere.

+ + + +

Quamvis enim depravatae non sint, pravae tamen esse possunt. Curium putes loqui, interdum ita laudat, ut quid praeterea sit bonum neget se posse ne suspicari quidem. Miserum hominem! Si dolor summum malum est, dici aliter non potest. Sed erat aequius Triarium aliquid de dissensione nostra iudicare. Hanc ergo intuens debet institutum illud quasi signum absolvere. Non autem hoc: igitur ne illud quidem. Quibus ego vehementer assentior. An dolor longissimus quisque miserrimus, voluptatem non optabiliorem diuturnitas facit?

+ + + +
Quae similitudo in genere etiam humano apparet.
+ + + +

Nam adhuc, meo fortasse vitio, quid ego quaeram non perspicis. Suam denique cuique naturam esse ad vivendum ducem.

+ + + +
Duo enim genera quae erant, fecit tria.
+ + + +

Nam Metrodorum non puto ipsum professum, sed, cum appellaretur ab Epicuro, repudiare tantum beneficium noluisse; Eadem nunc mea adversum te oratio est. Ut alios omittam, hunc appello, quem ille unum secutus est. Itaque nostrum est-quod nostrum dico, artis est-ad ea principia, quae accepimus. Sed utrum hortandus es nobis, Luci, inquit, an etiam tua sponte propensus es? Ipse Epicurus fortasse redderet, ut Sextus Peducaeus, Sex.

+ + + +

Quid est igitur, inquit, quod requiras? Ille enim occurrentia nescio quae comminiscebatur; Experiamur igitur, inquit, etsi habet haec Stoicorum ratio difficilius quiddam et obscurius. Cum salvum esse flentes sui respondissent, rogavit essentne fusi hostes.

+ + + +

Memini vero, inquam; Respondeat totidem verbis. Quae similitudo in genere etiam humano apparet. Ita fit cum gravior, tum etiam splendidior oratio. Unum nescio, quo modo possit, si luxuriosus sit, finitas cupiditates habere.

+ + + +
Quae cum dixisset paulumque institisset, Quid est?
+ + + +

Quae cum ita sint, effectum est nihil esse malum, quod turpe non sit. Quo modo autem optimum, si bonum praeterea nullum est? Si de re disceptari oportet, nulla mihi tecum, Cato, potest esse dissensio. Apparet statim, quae sint officia, quae actiones. Istam voluptatem, inquit, Epicurus ignorat? Hoc ille tuus non vult omnibusque ex rebus voluptatem quasi mercedem exigit. Eam stabilem appellas. Sed eum qui audiebant, quoad poterant, defendebant sententiam suam. Haec dicuntur inconstantissime. Suo genere perveniant ad extremum; Sed ille, ut dixi, vitiose.

+ + + +

Quid de Platone aut de Democrito loquar? Sed haec in pueris; Utrum igitur tibi litteram videor an totas paginas commovere? Summum ením bonum exposuit vacuitatem doloris; Memini vero, inquam; Quis non odit sordidos, vanos, leves, futtiles?

+ + + +

Sed eum qui audiebant, quoad poterant, defendebant sententiam suam.

+ + + +

Recte dicis; Sed haec in pueris; Quid ergo hoc loco intellegit honestum? Et quidem, inquit, vehementer errat; Prodest, inquit, mihi eo esse animo.

+ + + +

Quis istud, quaeso, nesciebat? Bonum integritas corporis: misera debilitas. Ut nemo dubitet, eorum omnia officia quo spectare, quid sequi, quid fugere debeant? Itaque sensibus rationem adiunxit et ratione effecta sensus non reliquit. Huius, Lyco, oratione locuples, rebus ipsis ielunior. Quod autem ratione actum est, id officium appellamus. Ergo hoc quidem apparet, nos ad agendum esse natos.

+ + + +
+ + + +

Suo genere perveniant ad extremum;

+ + + +

Quod vestri non item. Rationis enim perfectio est virtus; Quaeque de virtutibus dicta sunt, quem ad modum eae semper voluptatibus inhaererent, eadem de amicitia dicenda sunt. Odium autem et invidiam facile vitabis. Minime vero, inquit ille, consentit. Neque solum ea communia, verum etiam paria esse dixerunt.

+ + + +

Sed haec quidem liberius ab eo dicuntur et saepius. Paria sunt igitur. Num quid tale Democritus? Nam Pyrrho, Aristo, Erillus iam diu abiecti. Quid ei reliquisti, nisi te, quoquo modo loqueretur, intellegere, quid diceret? Mihi enim satis est, ipsis non satis. Itaque contra est, ac dicitis; Duarum enim vitarum nobis erunt instituta capienda.

+ + + +
+

+ Quibus expositis facilis est coniectura ea maxime esse expetenda ex nostris, quae plurimum habent dignitatis, ut optimae cuiusque partis, quae per se expetatur, virtus sit expetenda maxime. +

+
+ + + +

Atque adhuc ea dixi, causa cur Zenoni non fuisset, quam ob rem a superiorum auctoritate discederet. Quare conare, quaeso. Familiares nostros, credo, Sironem dicis et Philodemum, cum optimos viros, tum homines doctissimos.

+ + + +

Paulum, cum regem Persem captum adduceret, eodem flumine invectio?

+ + + +

Sin tantum modo ad indicia veteris memoriae cognoscenda, curiosorum. Vitae autem degendae ratio maxime quidem illis placuit quieta. Eiuro, inquit adridens, iniquum, hac quidem de re; Sed fortuna fortis; Quae duo sunt, unum facit. Uterque enim summo bono fruitur, id est voluptate. Quae qui non vident, nihil umquam magnum ac cognitione dignum amaverunt. Ita credo.

+ + + +

Quo plebiscito decreta a senatu est consuli quaestio Cn. Quid est igitur, cur ita semper deum appellet Epicurus beatum et aeternum? Quia nec honesto quic quam honestius nec turpi turpius. Gracchum patrem non beatiorem fuisse quam fillum, cum alter stabilire rem publicam studuerit, alter evertere. Bestiarum vero nullum iudicium puto. Peccata paria. Et quidem, inquit, vehementer errat; Deprehensus omnem poenam contemnet. Haec igitur Epicuri non probo, inquam.

+ + + +

Ergo omni animali illud, quod appetiti positum est in eo, quod naturae est accommodatum. Ita enim vivunt quidam, ut eorum vita refellatur oratio. Nam, ut sint illa vendibiliora, haec uberiora certe sunt. Quae quidem vel cum periculo est quaerenda vobis;

+ + + +

Aliter enim nosmet ipsos nosse non possumus.

+ + + +

Cur, nisi quod turpis oratio est? Vide, quantum, inquam, fallare, Torquate. Beatus sibi videtur esse moriens. Sic enim censent, oportunitatis esse beate vivere. Aut unde est hoc contritum vetustate proverbium: quicum in tenebris? Unum nescio, quo modo possit, si luxuriosus sit, finitas cupiditates habere.

+ + + +

Sed quot homines, tot sententiae; Quod si ita sit, cur opera philosophiae sit danda nescio. Cur id non ita fit? Quorum altera prosunt, nocent altera. Quippe: habes enim a rhetoribus; Amicitiae vero locus ubi esse potest aut quis amicus esse cuiquam, quem non ipsum amet propter ipsum? Parvi enim primo ortu sic iacent, tamquam omnino sine animo sint. Restinguet citius, si ardentem acceperit. Quod autem principium officii quaerunt, melius quam Pyrrho;

+ + + +

Idcirco enim non desideraret, quia, quod dolore caret, id in voluptate est.

+ + + +

Qui enim existimabit posse se miserum esse beatus non erit. Tollenda est atque extrahenda radicitus. Ergo illi intellegunt quid Epicurus dicat, ego non intellego? Si de re disceptari oportet, nulla mihi tecum, Cato, potest esse dissensio. Nos paucis ad haec additis finem faciamus aliquando; Nosti, credo, illud: Nemo pius est, qui pietatem-; Si quicquam extra virtutem habeatur in bonis. An me, inquam, nisi te audire vellem, censes haec dicturum fuisse?

+ + + +
    +
  • Pudebit te, inquam, illius tabulae, quam Cleanthes sane commode verbis depingere solebat.
  • + + + +
  • Huic mori optimum esse propter desperationem sapientiae, illi propter spem vivere.
  • + + + +
  • Quae hic rei publicae vulnera inponebat, eadem ille sanabat.
  • + + + +
  • Hoc est non modo cor non habere, sed ne palatum quidem.
  • +
+ + + +

Haeret in salebra. Respondent extrema primis, media utrisque, omnia omnibus.

+ + + +

Quis non odit sordidos, vanos, leves, futtiles? Post enim Chrysippum eum non sane est disputatum. Omnia contraria, quos etiam insanos esse vultis. Theophrastus mediocriterne delectat, cum tractat locos ab Aristotele ante tractatos? Sed ad haec, nisi molestum est, habeo quae velim. Simus igitur contenti his. Videmusne ut pueri ne verberibus quidem a contemplandis rebus perquirendisque deterreantur? Verum hoc idem saepe faciamus. In eo enim positum est id, quod dicimus esse expetendum. At enim hic etiam dolore.

+ + + +

Nec vero sum nescius esse utilitatem in historia, non modo voluptatem. Huic mori optimum esse propter desperationem sapientiae, illi propter spem vivere.

+ + + +

Oratio me istius philosophi non offendit; Is ita vivebat, ut nulla tam exquisita posset inveniri voluptas, qua non abundaret. Ita fit cum gravior, tum etiam splendidior oratio. Cur tantas regiones barbarorum pedibus obiit, tot maria transmisit? Quid igitur dubitamus in tota eius natura quaerere quid sit effectum? Nihilne est in his rebus, quod dignum libero aut indignum esse ducamus? Terram, mihi crede, ea lanx et maria deprimet.

+ + + +

Duo enim genera quae erant, fecit tria. In quibus doctissimi illi veteres inesse quiddam caeleste et divinum putaverunt. Suam denique cuique naturam esse ad vivendum ducem. Dicet pro me ipsa virtus nec dubitabit isti vestro beato M. Intellegi quidem, ut propter aliam quampiam rem, verbi gratia propter voluptatem, nos amemus; Quae tamen a te agetur non melior, quam illae sunt, quas interdum optines. At ille pellit, qui permulcet sensum voluptate. Illi enim inter se dissentiunt.

+ + + +

Quamvis enim depravatae non sint, pravae tamen esse possunt. Aut, Pylades cum sis, dices te esse Orestem, ut moriare pro amico? Nobis aliter videtur, recte secusne, postea; Quid iudicant sensus? Teneo, inquit, finem illi videri nihil dolere. Fortemne possumus dicere eundem illum Torquatum?

+ + + +

Mihi enim satis est, ipsis non satis.

+ + + +

Gloriosa ostentatio in constituendo summo bono. Nulla erit controversia. Nemo nostrum istius generis asotos iucunde putat vivere. Qui-vere falsone, quaerere mittimus-dicitur oculis se privasse; Ut pulsi recurrant? Negare non possum.

+ + + +

Quae est igitur causa istarum angustiarum?

+ + + +

Ut non sine causa ex iis memoriae ducta sit disciplina. Faceres tu quidem, Torquate, haec omnia; Eadem nunc mea adversum te oratio est. Sedulo, inquam, faciam. Recte, inquit, intellegis. Legimus tamen Diogenem, Antipatrum, Mnesarchum, Panaetium, multos alios in primisque familiarem nostrum Posidonium. Ego vero volo in virtute vim esse quam maximam;

+ + + +
Heri, inquam, ludis commissis ex urbe profectus veni ad vesperum.
+ + + +

Cupit enim dícere nihil posse ad beatam vitam deesse sapienti. Nondum autem explanatum satis, erat, quid maxime natura vellet. Quae sunt igitur communia vobis cum antiquis, iis sic utamur quasi concessis; Incommoda autem et commoda-ita enim estmata et dustmata appello-communia esse voluerunt, paria noluerunt. Quid me istud rogas? Nam et complectitur verbis, quod vult, et dicit plane, quod intellegam; Ergo opifex plus sibi proponet ad formarum quam civis excellens ad factorum pulchritudinem? Paulum, cum regem Persem captum adduceret, eodem flumine invectio? Potius ergo illa dicantur: turpe esse, viri non esse debilitari dolore, frangi, succumbere. Sit, inquam, tam facilis, quam vultis, comparatio voluptatis, quid de dolore dicemus?

+ + + +

Pauca mutat vel plura sane;

+ + + +

An eum discere ea mavis, quae cum plane perdidiceriti nihil sciat? Similiter sensus, cum accessit ad naturam, tuetur illam quidem, sed etiam se tuetur; Quis non odit sordidos, vanos, leves, futtiles? Negat esse eam, inquit, propter se expetendam. Quis Aristidem non mortuum diligit? Certe non potest.

+ + + +

Ea possunt paria non esse. Quem ad modum quis ambulet, sedeat, qui ductus oris, qui vultus in quoque sit? Si longus, levis; Eodem modo is enim tibi nemo dabit, quod, expetendum sit, id esse laudabile. Itaque his sapiens semper vacabit. Alterum significari idem, ut si diceretur, officia media omnia aut pleraque servantem vivere. Sed ille, ut dixi, vitiose. Quis est enim, in quo sit cupiditas, quin recte cupidus dici possit?

+ + + +

Collige omnia, quae soletis: Praesidium amicorum.

+ + + +

An vero displicuit ea, quae tributa est animi virtutibus tanta praestantia? Quare hoc videndum est, possitne nobis hoc ratio philosophorum dare. Quae contraria sunt his, malane? Oculorum, inquit Plato, est in nobis sensus acerrimus, quibus sapientiam non cernimus. Neminem videbis ita laudatum, ut artifex callidus comparandarum voluptatum diceretur. Iis igitur est difficilius satis facere, qui se Latina scripta dicunt contemnere. Nec vero sum nescius esse utilitatem in historia, non modo voluptatem. Itaque vides, quo modo loquantur, nova verba fingunt, deserunt usitata.

+ + + +
Non laboro, inquit, de nomine.
+ + + +

Ut optime, secundum naturam affectum esse possit. Si enim ad populum me vocas, eum. Quid de Platone aut de Democrito loquar?

+ + + +
Quae quo sunt excelsiores, eo dant clariora indicia naturae.
+ + + +

Itaque haec cum illis est dissensio, cum Peripateticis nulla sane. Piso igitur hoc modo, vir optimus tuique, ut scis, amantissimus. Laboro autem non sine causa; Aliter homines, aliter philosophos loqui putas oportere? Quid enim mihi potest esse optatius quam cum Catone, omnium virtutum auctore, de virtutibus disputare? Est, ut dicis, inquam. Collatio igitur ista te nihil iuvat. Sic consequentibus vestris sublatis prima tolluntur. Dolor ergo, id est summum malum, metuetur semper, etiamsi non aderit; Sed ille, ut dixi, vitiose.

+ + + +
    +
  • Haec qui audierit, ut ridere non curet, discedet tamen nihilo firmior ad dolorem ferendum, quam venerat.
  • + + + +
  • Sic, et quidem diligentius saepiusque ista loquemur inter nos agemusque communiter.
  • + + + +
  • Nec vero sum nescius esse utilitatem in historia, non modo voluptatem.
  • + + + +
  • Laelius clamores sofòw ille so lebat Edere compellans gumias ex ordine nostros.
  • +
+ + + +

Nam illud vehementer repugnat, eundem beatum esse et multis malis oppressum. Te enim iudicem aequum puto, modo quae dicat ille bene noris. Similiter sensus, cum accessit ad naturam, tuetur illam quidem, sed etiam se tuetur;

+ + + +
    +
  • Si enim ita est, vide ne facinus facias, cum mori suadeas.
  • + + + +
  • Quis Aristidem non mortuum diligit?
  • + + + +
  • Quos quidem tibi studiose et diligenter tractandos magnopere censeo.
  • + + + +
  • Sin te auctoritas commovebat, nobisne omnibus et Platoni ipsi nescio quem illum anteponebas?
  • +
+ + + +

Ex ea difficultate illae fallaciloquae, ut ait Accius, malitiae natae sunt. Nec tamen ille erat sapiens quis enim hoc aut quando aut ubi aut unde? Quorum altera prosunt, nocent altera. An est aliquid per se ipsum flagitiosum, etiamsi nulla comitetur infamia? Itaque ad tempus ad Pisonem omnes. Disserendi artem nullam habuit. Et harum quidem rerum facilis est et expedita distinctio. Erat enim Polemonis. Huius ego nunc auctoritatem sequens idem faciam.

+ + + +

Quem ad modum quis ambulet, sedeat, qui ductus oris, qui vultus in quoque sit?

+ + + +

Beatus autem esse in maximarum rerum timore nemo potest. Sed quid minus probandum quam esse aliquem beatum nec satis beatum? Quem si tenueris, non modo meum Ciceronem, sed etiam me ipsum abducas licebit. Themistocles quidem, cum ei Simonides an quis alius artem memoriae polliceretur, Oblivionis, inquit, mallem. Quorum sine causa fieri nihil putandum est. Illa argumenta propria videamus, cur omnia sint paria peccata. Nunc de hominis summo bono quaeritur; Quare attendo te studiose et, quaecumque rebus iis, de quibus hic sermo est, nomina inponis, memoriae mando; Hoc non est positum in nostra actione. Quare hoc videndum est, possitne nobis hoc ratio philosophorum dare.

+ + + +

Duarum enim vitarum nobis erunt instituta capienda. At enim sequor utilitatem. Et ille ridens: Video, inquit, quid agas; Post enim Chrysippum eum non sane est disputatum. Eadem nunc mea adversum te oratio est.

+ + + +

Et quidem, inquit, vehementer errat;

+ + + +

Omnis enim est natura diligens sui. Quid ad utilitatem tantae pecuniae? Graecis hoc modicum est: Leonidas, Epaminondas, tres aliqui aut quattuor; Hic ego: Pomponius quidem, inquam, noster iocari videtur, et fortasse suo iure. Modo etiam paulum ad dexteram de via declinavi, ut ad Pericli sepulcrum accederem. Sed ad rem redeamus; In eo enim positum est id, quod dicimus esse expetendum. At quicum ioca seria, ut dicitur, quicum arcana, quicum occulta omnia?

+ + + +

Quid igitur dubitamus in tota eius natura quaerere quid sit effectum? Immo vero, inquit, ad beatissime vivendum parum est, ad beate vero satis. Hic quoque suus est de summoque bono dissentiens dici vere Peripateticus non potest. Dolere malum est: in crucem qui agitur, beatus esse non potest. Ut proverbia non nulla veriora sint quam vestra dogmata. Nec tamen ullo modo summum pecudis bonum et hominis idem mihi videri potest.

+ + + +

Quod cum accidisset ut alter alterum necopinato videremus, surrexit statim. Pauca mutat vel plura sane; Nam et a te perfici istam disputationem volo, nec tua mihi oratio longa videri potest. Aut haec tibi, Torquate, sunt vituperanda aut patrocinium voluptatis repudiandum. Qui non moveatur et offensione turpitudinis et comprobatione honestatis? Nescio quo modo praetervolavit oratio. Ita ceterorum sententiis semotis relinquitur non mihi cum Torquato, sed virtuti cum voluptate certatio. Qui enim voluptatem ipsam contemnunt, iis licet dicere se acupenserem maenae non anteponere.

+ + + +

Aliena dixit in physicis nec ea ipsa, quae tibi probarentur; Itaque et manendi in vita et migrandi ratio omnis iis rebus, quas supra dixi, metienda. An quod ita callida est, ut optime possit architectari voluptates? Qui enim voluptatem ipsam contemnunt, iis licet dicere se acupenserem maenae non anteponere.

+ + + +

Non enim, si omnia non sequebatur, idcirco non erat ortus illinc. An ea, quae per vinitorem antea consequebatur, per se ipsa curabit? Tamen a proposito, inquam, aberramus. Ex rebus enim timiditas, non ex vocabulis nascitur. Ut in geometria, prima si dederis, danda sunt omnia. Reguli reiciendam;

+ + + +
+

+ Illud urgueam, non intellegere eum quid sibi dicendum sit, cum dolorem summum malum esse dixerit. +

+
+ + + +

Ergo adhuc, quantum equidem intellego, causa non videtur fuisse mutandi nominis.

+ + + +

Hic Speusippus, hic Xenocrates, hic eius auditor Polemo, cuius illa ipsa sessio fuit, quam videmus. Poterat autem inpune; Nec enim ignoras his istud honestum non summum modo, sed etiam, ut tu vis, solum bonum videri. Praeclare hoc quidem. Iam enim adesse poterit. Scientiam pollicentur, quam non erat mirum sapientiae cupido patria esse cariorem. Aliter enim nosmet ipsos nosse non possumus. Restincta enim sitis stabilitatem voluptatis habet, inquit, illa autem voluptas ipsius restinctionis in motu est. At negat Epicurus-hoc enim vestrum lumen estquemquam, qui honeste non vivat, iucunde posse vivere.

+ + + +

Servari enim iustitia nisi a forti viro, nisi a sapiente non potest. Sint modo partes vitae beatae. Bestiarum vero nullum iudicium puto. Et certamen honestum et disputatio splendida! omnis est enim de virtutis dignitate contentio.

+ + + +

Non autem hoc: igitur ne illud quidem.

+ + + +

Ut optime, secundum naturam affectum esse possit. Nec vero alia sunt quaerenda contra Carneadeam illam sententiam. Non potes, nisi retexueris illa. Sit enim idem caecus, debilis. Ne amores quidem sanctos a sapiente alienos esse arbitrantur. Cur deinde Metrodori liberos commendas?

+ + + +
    +
  • Videmusne ut pueri ne verberibus quidem a contemplandis rebus perquirendisque deterreantur?
  • + + + +
  • Quod si ita se habeat, non possit beatam praestare vitam sapientia.
  • +
+ + + +
Easdemne res?
+ + + +

Videamus animi partes, quarum est conspectus illustrior; Cur igitur easdem res, inquam, Peripateticis dicentibus verbum nullum est, quod non intellegatur? Quod dicit Epicurus etiam de voluptate, quae minime sint voluptates, eas obscurari saepe et obrui. Est, ut dicis, inquit; Sed utrum hortandus es nobis, Luci, inquit, an etiam tua sponte propensus es? Itaque hic ipse iam pridem est reiectus; Illud dico, ea, quae dicat, praeclare inter se cohaerere. Sed videbimus. Est igitur officium eius generis, quod nec in bonis ponatur nec in contrariis. Quae cum essent dicta, discessimus.

+ + + +

Comprehensum, quod cognitum non habet? Prave, nequiter, turpiter cenabat; Quae autem natura suae primae institutionis oblita est? Respondeat totidem verbis. Nec vero alia sunt quaerenda contra Carneadeam illam sententiam. Prave, nequiter, turpiter cenabat; Qui-vere falsone, quaerere mittimus-dicitur oculis se privasse; Itaque hic ipse iam pridem est reiectus; Dicimus aliquem hilare vivere; Occultum facinus esse potuerit, gaudebit;

+ + + +
+ + + +

Tubulum fuisse, qua illum, cuius is condemnatus est rogatione, P. Profectus in exilium Tubulus statim nec respondere ausus; Ut in geometria, prima si dederis, danda sunt omnia. Omnia contraria, quos etiam insanos esse vultis. Quae similitudo in genere etiam humano apparet. Sed nimis multa. Hoc mihi cum tuo fratre convenit. Quid, quod homines infima fortuna, nulla spe rerum gerendarum, opifices denique delectantur historia? Quae est igitur causa istarum angustiarum?

+ + + +

Est, ut dicis, inquam. Quid turpius quam sapientis vitam ex insipientium sermone pendere? Sed ad haec, nisi molestum est, habeo quae velim. At ille non pertimuit saneque fidenter: Istis quidem ipsis verbis, inquit; Cenasti in vita numquam bene, cum omnia in ista Consumis squilla atque acupensere cum decimano. Laboro autem non sine causa; Conferam avum tuum Drusum cum C. Cur post Tarentum ad Archytam? Quid de Pythagora? Quam ob rem tandem, inquit, non satisfacit?

+ + + +

Tum mihi Piso: Quid ergo? Scio enim esse quosdam, qui quavis lingua philosophari possint; Sed finge non solum callidum eum, qui aliquid improbe faciat, verum etiam praepotentem, ut M. Igitur neque stultorum quisquam beatus neque sapientium non beatus. Mihi vero, inquit, placet agi subtilius et, ut ipse dixisti, pressius. Expectoque quid ad id, quod quaerebam, respondeas. Ita relinquet duas, de quibus etiam atque etiam consideret. Scaevola tribunus plebis ferret ad plebem vellentne de ea re quaeri. Ecce aliud simile dissimile. Hoc loco tenere se Triarius non potuit.

+ + + +
Quae cum praeponunt, ut sit aliqua rerum selectio, naturam videntur sequi;
+ + + +

Ego quoque, inquit, didicerim libentius si quid attuleris, quam te reprehenderim. Nihil enim hoc differt. Eaedem enim utilitates poterunt eas labefactare atque pervertere.

+ + + +
+

+ Nam quod ita positum est, quod dissolutum sit, id esse sine sensu, id eius modi est, ut non satis plane dicat quid sit dissolutum. +

+
+ + + +

Ratio quidem vestra sic cogit.

+ + + +

Quia voluptatem hanc esse sentiunt omnes, quam sensus accipiens movetur et iucunditate quadam perfunditur. Cum id quoque, ut cupiebat, audivisset, evelli iussit eam, qua erat transfixus, hastam. At cum de plurimis eadem dicit, tum certe de maximis. At iste non dolendi status non vocatur voluptas. Utinam quidem dicerent alium alio beatiorem! Iam ruinas videres. Quod idem cum vestri faciant, non satis magnam tribuunt inventoribus gratiam. Qua igitur re ab deo vincitur, si aeternitate non vincitur? Sic consequentibus vestris sublatis prima tolluntur.

+ + + +

Ego vero volo in virtute vim esse quam maximam; Quodsi ipsam honestatem undique pertectam atque absolutam. Satis est tibi in te, satis in legibus, satis in mediocribus amicitiis praesidii. Quem ad modum quis ambulet, sedeat, qui ductus oris, qui vultus in quoque sit? Licet hic rursus ea commemores, quae optimis verbis ab Epicuro de laude amicitiae dicta sunt. Itaque ab his ordiamur. Dici enim nihil potest verius. Idem iste, inquam, de voluptate quid sentit? Illud dico, ea, quae dicat, praeclare inter se cohaerere. Quis est tam dissimile homini.

+ + + +

Tertium autem omnibus aut maximis rebus iis, quae secundum naturam sint, fruentem vivere. Bonum patria: miserum exilium. Quid est, quod ab ea absolvi et perfici debeat? Habent enim et bene longam et satis litigiosam disputationem. Nondum autem explanatum satis, erat, quid maxime natura vellet. Egone quaeris, inquit, quid sentiam? Facillimum id quidem est, inquam. Nihil opus est exemplis hoc facere longius. Piso, familiaris noster, et alia multa et hoc loco Stoicos irridebat: Quid enim?

+ + + +
    +
  • Quando enim Socrates, qui parens philosophiae iure dici potest, quicquam tale fecit?
  • + + + +
  • Ne vitationem quidem doloris ipsam per se quisquam in rebus expetendis putavit, nisi etiam evitare posset.
  • +
+ + + +

Omnes enim iucundum motum, quo sensus hilaretur. Illa tamen simplicia, vestra versuta. Id mihi magnum videtur. Habes, inquam, Cato, formam eorum, de quibus loquor, philosophorum. At enim hic etiam dolore. Eaedem res maneant alio modo. Atque haec ita iustitiae propria sunt, ut sint virtutum reliquarum communia. Sapiens autem semper beatus est et est aliquando in dolore; Haeret in salebra. Ita graviter et severe voluptatem secrevit a bono. Quae cum dixisset paulumque institisset, Quid est?

+ + + +

Quarum ambarum rerum cum medicinam pollicetur, luxuriae licentiam pollicetur. Ita enim vivunt quidam, ut eorum vita refellatur oratio. Utrum igitur tibi litteram videor an totas paginas commovere? Praeterea et appetendi et refugiendi et omnino rerum gerendarum initia proficiscuntur aut a voluptate aut a dolore. Nisi autem rerum natura perspecta erit, nullo modo poterimus sensuum iudicia defendere. Sed non sunt in eo genere tantae commoditates corporis tamque productae temporibus tamque multae. Hoc positum in Phaedro a Platone probavit Epicurus sensitque in omni disputatione id fieri oportere. Huius, Lyco, oratione locuples, rebus ipsis ielunior. Sic, et quidem diligentius saepiusque ista loquemur inter nos agemusque communiter.

+ + + +

Ac tamen hic mallet non dolere.

+ + + +

Perturbationes autem nulla naturae vi commoventur, omniaque ea sunt opiniones ac iudicia levitatis. Indicant pueri, in quibus ut in speculis natura cernitur. Nos commodius agimus. Quae duo sunt, unum facit. Tum ille: Tu autem cum ipse tantum librorum habeas, quos hic tandem requiris? Neque solum ea communia, verum etiam paria esse dixerunt. Verba tu fingas et ea dicas, quae non sentias?

+ + + +

Ad corpus diceres pertinere-, sed ea, quae dixi, ad corpusne refers? Quonam, inquit, modo? Utilitatis causa amicitia est quaesita. Qui-vere falsone, quaerere mittimus-dicitur oculis se privasse; Hi autem ponunt illi quidem prima naturae, sed ea seiungunt a finibus et a summa bonorum;

+ + + +

Quasi ego id curem, quid ille aiat aut neget.

+ + + +

Honesta oratio, Socratica, Platonis etiam. Sapiens autem semper beatus est et est aliquando in dolore;

+ + + +

Illis videtur, qui illud non dubitant bonum dicere -; Hoc non est positum in nostra actione. Quae diligentissime contra Aristonem dicuntur a Chryippo. Nec vero hoc oratione solum, sed multo magis vita et factis et moribus comprobavit. Tum mihi Piso: Quid ergo? Sed potestne rerum maior esse dissensio? Haec dicuntur fortasse ieiunius; Qualem igitur hominem natura inchoavit? Atqui iste locus est, Piso, tibi etiam atque etiam confirmandus, inquam; Quid turpius quam sapientis vitam ex insipientium sermone pendere?

+ + + +

Ad eas enim res ab Epicuro praecepta dantur. Ita redarguitur ipse a sese, convincunturque scripta eius probitate ipsius ac moribus. Negat esse eam, inquit, propter se expetendam. Expectoque quid ad id, quod quaerebam, respondeas. Potius inflammat, ut coercendi magis quam dedocendi esse videantur. Prioris generis est docilitas, memoria; Nunc ita separantur, ut disiuncta sint, quo nihil potest esse perversius. Itaque hic ipse iam pridem est reiectus;

+ + + +
    +
  • Philosophi autem in suis lectulis plerumque moriuntur.
  • + + + +
  • Bona autem corporis huic sunt, quod posterius posui, similiora.
  • + + + +
  • Quae in controversiam veniunt, de iis, si placet, disseramus.
  • +
+ + + +
Rapior illuc, revocat autem Antiochus, nec est praeterea, quem audiamus.
+ + + +

Quibus ego vehementer assentior. Licet hic rursus ea commemores, quae optimis verbis ab Epicuro de laude amicitiae dicta sunt. Cum id fugiunt, re eadem defendunt, quae Peripatetici, verba. Quae sunt igitur communia vobis cum antiquis, iis sic utamur quasi concessis; Mihi enim erit isdem istis fortasse iam utendum. Quantum Aristoxeni ingenium consumptum videmus in musicis? Si quae forte-possumus. Equidem etiam Epicurum, in physicis quidem, Democriteum puto. Quis negat? Equidem soleo etiam quod uno Graeci, si aliter non possum, idem pluribus verbis exponere.

+ + + +

Tecum optime, deinde etiam cum mediocri amico. Bonum integritas corporis: misera debilitas. Mihi, inquam, qui te id ipsum rogavi? Ut enim consuetudo loquitur, id solum dicitur honestum, quod est populari fama gloriosum. Bonum incolumis acies: misera caecitas. Ut proverbia non nulla veriora sint quam vestra dogmata. Claudii libidini, qui tum erat summo ne imperio, dederetur. Non est enim vitium in oratione solum, sed etiam in moribus.

+ + + +

Color egregius, integra valitudo, summa gratia, vita denique conferta voluptatum omnium varietate. Maximus dolor, inquit, brevis est. Nunc haec primum fortasse audientis servire debemus. Luxuriam non reprehendit, modo sit vacua infinita cupiditate et timore. Frater et T. Haec bene dicuntur, nec ego repugno, sed inter sese ipsa pugnant. Idem iste, inquam, de voluptate quid sentit?

+ + + +

Unum nescio, quo modo possit, si luxuriosus sit, finitas cupiditates habere.

+ + + +

Vos autem cum perspicuis dubia debeatis illustrare, dubiis perspicua conamini tollere. Maximas vero virtutes iacere omnis necesse est voluptate dominante. Nam si amitti vita beata potest, beata esse non potest. Memini vero, inquam; Quo modo autem optimum, si bonum praeterea nullum est? Satis est tibi in te, satis in legibus, satis in mediocribus amicitiis praesidii.

+ + + +
    +
  • Zenonis est, inquam, hoc Stoici.
  • + + + +
  • Cum sciret confestim esse moriendum eamque mortem ardentiore studio peteret, quam Epicurus voluptatem petendam putat.
  • + + + +
  • Hoc loco discipulos quaerere videtur, ut, qui asoti esse velint, philosophi ante fiant.
  • + + + +
  • Polemoni et iam ante Aristoteli ea prima visa sunt, quae paulo ante dixi.
  • +
+ + + +
+

+ Maximeque eos videre possumus res gestas audire et legere velle, qui a spe gerendi absunt confecti senectute. +

+
+ + + +

Nam Pyrrho, Aristo, Erillus iam diu abiecti. Equidem, sed audistine modo de Carneade? Et ille ridens: Video, inquit, quid agas; Nulla profecto est, quin suam vim retineat a primo ad extremum. Conferam tecum, quam cuique verso rem subicias; Nihil sane. Qui est in parvis malis. Quid enim me prohiberet Epicureum esse, si probarem, quae ille diceret?

+ + + +

Restatis igitur vos; Ita graviter et severe voluptatem secrevit a bono. Equidem, sed audistine modo de Carneade? Sed in rebus apertissimis nimium longi sumus. Ecce aliud simile dissimile. Nulla profecto est, quin suam vim retineat a primo ad extremum.

+ + + +

Iubet igitur nos Pythius Apollo noscere nosmet ipsos.

+ + + +

Quantum Aristoxeni ingenium consumptum videmus in musicis? Itaque nostrum est-quod nostrum dico, artis est-ad ea principia, quae accepimus. Nam memini etiam quae nolo, oblivisci non possum quae volo. Cur, nisi quod turpis oratio est? Quam tu ponis in verbis, ego positam in re putabam. Quae cum ita sint, effectum est nihil esse malum, quod turpe non sit.

+ + + +
+

+ Nam quod ita positum est, quod dissolutum sit, id esse sine sensu, id eius modi est, ut non satis plane dicat quid sit dissolutum. +

+
+ + + +

Ubi ut eam caperet aut quando? Sin kakan malitiam dixisses, ad aliud nos unum certum vitium consuetudo Latina traduceret. Videmusne ut pueri ne verberibus quidem a contemplandis rebus perquirendisque deterreantur? Parvi enim primo ortu sic iacent, tamquam omnino sine animo sint.

+ + + +
    +
  • Quae rursus dum sibi evelli ex ordine nolunt, horridiores evadunt, asperiores, duriores et oratione et moribus.
  • + + + +
  • In ipsa enim parum magna vis inest, ut quam optime se habere possit, si nulla cultura adhibeatur.
  • + + + +
  • Nec vero intermittunt aut admirationem earum rerum, quae sunt ab antiquis repertae, aut investigationem novarum.
  • + + + +
  • Itaque ab his ordiamur.
  • +
+ + + +
    +
  • Sic enim censent, oportunitatis esse beate vivere.
  • + + + +
  • Fortitudinis quaedam praecepta sunt ac paene leges, quae effeminari virum vetant in dolore.
  • + + + +
  • Quod si ita se habeat, non possit beatam praestare vitam sapientia.
  • + + + +
  • Quicquid enim a sapientia proficiscitur, id continuo debet expletum esse omnibus suis partibus;
  • +
+ + + +

Ergo ita: non posse honeste vivi, nisi honeste vivatur? Aperiendum est igitur, quid sit voluptas; Itaque his sapiens semper vacabit. Sed ad haec, nisi molestum est, habeo quae velim. Ergo hoc quidem apparet, nos ad agendum esse natos. Quis est enim, in quo sit cupiditas, quin recte cupidus dici possit? In quo etsi est magnus, tamen nova pleraque et perpauca de moribus. Iam in altera philosophiae parte. De hominibus dici non necesse est.

+ + + +
+

+ Haec ego non possum dicere non esse hominis quamvis et belli et humani, sapientis vero nullo modo, physici praesertim, quem se ille esse vult, putare ullum esse cuiusquam diem natalem. +

+
+ + + +
Qualem igitur hominem natura inchoavit?
+ + + +

In qua si nihil est praeter rationem, sit in una virtute finis bonorum; Nec tamen ullo modo summum pecudis bonum et hominis idem mihi videri potest. Nulla profecto est, quin suam vim retineat a primo ad extremum.

+ + + +

Tubulo putas dicere?

+ + + +

Cum sciret confestim esse moriendum eamque mortem ardentiore studio peteret, quam Epicurus voluptatem petendam putat. Ergo illi intellegunt quid Epicurus dicat, ego non intellego? Qui igitur convenit ab alia voluptate dicere naturam proficisci, in alia summum bonum ponere? Teneo, inquit, finem illi videri nihil dolere. Quo minus animus a se ipse dissidens secumque discordans gustare partem ullam liquidae voluptatis et liberae potest.

+ + + +

Et ille ridens: Video, inquit, quid agas;

+ + + +

Certe nihil nisi quod possit ipsum propter se iure laudari. Et quidem, Cato, hanc totam copiam iam Lucullo nostro notam esse oportebit; Tamen a proposito, inquam, aberramus. Nam memini etiam quae nolo, oblivisci non possum quae volo. Amicitiae vero locus ubi esse potest aut quis amicus esse cuiquam, quem non ipsum amet propter ipsum? Satis est ad hoc responsum. De ingenio eius in his disputationibus, non de moribus quaeritur. Ab hoc autem quaedam non melius quam veteres, quaedam omnino relicta.

+ + + +

Illum mallem levares, quo optimum atque humanissimum virum, Cn.

+ + + +

Illi enim inter se dissentiunt. Quid, quod homines infima fortuna, nulla spe rerum gerendarum, opifices denique delectantur historia? Itaque his sapiens semper vacabit. Hunc vos beatum; Equidem e Cn. Verum enim diceret, idque Socratem, qui voluptatem nullo loco numerat, audio dicentem, cibi condimentum esse famem, potionis sitim. An haec ab eo non dicuntur? Collatio igitur ista te nihil iuvat. Si stante, hoc natura videlicet vult, salvam esse se, quod concedimus; Iam id ipsum absurdum, maximum malum neglegi. Vidit Homerus probari fabulam non posse, si cantiunculis tantus irretitus vir teneretur; Sensibus enim ornavit ad res percipiendas idoneis, ut nihil aut non multum adiumento ullo ad suam confirmationem indigerent;

+ + + +

Istic sum, inquit. Nullus est igitur cuiusquam dies natalis. Inde sermone vario sex illa a Dipylo stadia confecimus. Nam de isto magna dissensio est. At enim hic etiam dolore. Nunc ita separantur, ut disiuncta sint, quo nihil potest esse perversius. Ergo infelix una molestia, fellx rursus, cum is ipse anulus in praecordiis piscis inventus est? Sed tamen intellego quid velit.

+ + + +

Sed mehercule pergrata mihi oratio tua. Cur ipse Pythagoras et Aegyptum lustravit et Persarum magos adiit? Non pugnem cum homine, cur tantum habeat in natura boni; Cur igitur easdem res, inquam, Peripateticis dicentibus verbum nullum est, quod non intellegatur? At modo dixeras nihil in istis rebus esse, quod interesset. Non enim, si malum est dolor, carere eo malo satis est ad bene vivendum. Quaero igitur, quo modo hae tantae commendationes a natura profectae subito a sapientia relictae sint. Huic ego, si negaret quicquam interesse ad beate vivendum quali uteretur victu, concederem, laudarem etiam; Dolere malum est: in crucem qui agitur, beatus esse non potest. Nam quibus rebus efficiuntur voluptates, eae non sunt in potestate sapientis.

+ + + +

Quare hoc videndum est, possitne nobis hoc ratio philosophorum dare. Omnia contraria, quos etiam insanos esse vultis. Quod autem satis est, eo quicquid accessit, nimium est; Res enim fortasse verae, certe graves, non ita tractantur, ut debent, sed aliquanto minutius. Si longus, levis; Quis est tam dissimile homini. Qui-vere falsone, quaerere mittimus-dicitur oculis se privasse; Non pugnem cum homine, cur tantum habeat in natura boni; Habes, inquam, Cato, formam eorum, de quibus loquor, philosophorum.

+ + + +
Cur, nisi quod turpis oratio est?
+ + + +

Ut nemo dubitet, eorum omnia officia quo spectare, quid sequi, quid fugere debeant? Non enim in selectione virtus ponenda erat, ut id ipsum, quod erat bonorum ultimum, aliud aliquid adquireret. Sit enim idem caecus, debilis. Expressa vero in iis aetatibus, quae iam confirmatae sunt. At, illa, ut vobis placet, partem quandam tuetur, reliquam deserit. Hoc dixerit potius Ennius: Nimium boni est, cui nihil est mali. Si quidem, inquit, tollerem, sed relinquo.

+ + + +

Ego vero volo in virtute vim esse quam maximam;

+ + + +

Virtutis, magnitudinis animi, patientiae, fortitudinis fomentis dolor mitigari solet. Cum id fugiunt, re eadem defendunt, quae Peripatetici, verba. Laboro autem non sine causa; Quod, inquit, quamquam voluptatibus quibusdam est saepe iucundius, tamen expetitur propter voluptatem. An hoc usque quaque, aliter in vita? Sed quot homines, tot sententiae; Ut necesse sit omnium rerum, quae natura vigeant, similem esse finem, non eundem. Confecta res esset. Ita fit cum gravior, tum etiam splendidior oratio.

+ + + +

Mihi enim erit isdem istis fortasse iam utendum.

+ + + +

Non potes, nisi retexueris illa. In contemplatione et cognitione posita rerum, quae quia deorum erat vitae simillima, sapiente visa est dignissima. Sed virtutem ipsam inchoavit, nihil amplius. Sedulo, inquam, faciam. Nam constitui virtus nullo modo potesti nisi ea, quae sunt prima naturae, ut ad summam pertinentia tenebit. Invidiosum nomen est, infame, suspectum.

+ + + +
    +
  • Magni enim aestimabat pecuniam non modo non contra leges, sed etiam legibus partam.
  • + + + +
  • Erit enim mecum, si tecum erit.
  • + + + +
  • Ex rebus enim timiditas, non ex vocabulis nascitur.
  • +
+ + + +
+

+ Nihilne te delectat umquam -video, quicum loquar-, te igitur, Torquate, ipsum per se nihil delectat? +

+
+ + + +

Et quidem iure fortasse, sed tamen non gravissimum est testimonium multitudinis. Non enim quaero quid verum, sed quid cuique dicendum sit. Honesta oratio, Socratica, Platonis etiam. Haec bene dicuntur, nec ego repugno, sed inter sese ipsa pugnant. Quare attende, quaeso. Tubulo putas dicere? Facillimum id quidem est, inquam.

+ + + +

At quicum ioca seria, ut dicitur, quicum arcana, quicum occulta omnia? Et quidem saepe quaerimus verbum Latinum par Graeco et quod idem valeat; Virtutibus igitur rectissime mihi videris et ad consuetudinem nostrae orationis vitia posuisse contraria. Quae cum ita sint, effectum est nihil esse malum, quod turpe non sit. Summum ením bonum exposuit vacuitatem doloris; Itaque dicunt nec dubitant: mihi sic usus est, tibi ut opus est facto, fac. Mihi vero, inquit, placet agi subtilius et, ut ipse dixisti, pressius. Sed id ne cogitari quidem potest quale sit, ut non repugnet ipsum sibi. Fieri, inquam, Triari, nullo pacto potest, ut non dicas, quid non probes eius, a quo dissentias. Quippe: habes enim a rhetoribus; Sin aliud quid voles, postea. Non dolere, inquam, istud quam vim habeat postea videro;

+ + + +
+

+ Scripta sane et multa et polita, sed nescio quo pacto auctoritatem oratio non habet. +

+
+ + + +
    +
  • Quamquam tu hanc copiosiorem etiam soles dicere.
  • + + + +
  • Hic ambiguo ludimur.
  • + + + +
  • Ipse Epicurus fortasse redderet, ut Sextus Peducaeus, Sex.
  • + + + +
  • Apparet statim, quae sint officia, quae actiones.
  • + + + +
  • Curium putes loqui, interdum ita laudat, ut quid praeterea sit bonum neget se posse ne suspicari quidem.
  • + + + +
  • Et non ex maxima parte de tota iudicabis?
  • +
+ + + +
    +
  • Sin laboramus, quis est, qui alienae modum statuat industriae?
  • + + + +
  • Facit enim ille duo seiuncta ultima bonorum, quae ut essent vera, coniungi debuerunt;
  • + + + +
  • Qui non moveatur et offensione turpitudinis et comprobatione honestatis?
  • + + + +
  • Atqui reperies, inquit, in hoc quidem pertinacem;
  • +
+ + + +

Mihi, inquam, qui te id ipsum rogavi? Id enim natura desiderat. Septem autem illi non suo, sed populorum suffragio omnium nominati sunt. Ex eorum enim scriptis et institutis cum omnis doctrina liberalis, omnis historia. Non dolere, inquam, istud quam vim habeat postea videro; Levatio igitur vitiorum magna fit in iis, qui habent ad virtutem progressionis aliquantum. Itaque et manendi in vita et migrandi ratio omnis iis rebus, quas supra dixi, metienda. Summum ením bonum exposuit vacuitatem doloris; Stoici scilicet. Eaedem res maneant alio modo.

+ + + +

Tu quidem reddes; Haec para/doca illi, nos admirabilia dicamus. Idemne potest esse dies saepius, qui semel fuit? Quae contraria sunt his, malane? Nunc ita separantur, ut disiuncta sint, quo nihil potest esse perversius. Certe non potest. Mihi, inquam, qui te id ipsum rogavi? Hanc ergo intuens debet institutum illud quasi signum absolvere. Portenta haec esse dicit, neque ea ratione ullo modo posse vivi; Quid ergo attinet gloriose loqui, nisi constanter loquare?

+ + + +

Quo studio Aristophanem putamus aetatem in litteris duxisse?

+ + + +

Teneo, inquit, finem illi videri nihil dolere. Graece donan, Latine voluptatem vocant. Itaque his sapiens semper vacabit. At ille non pertimuit saneque fidenter: Istis quidem ipsis verbis, inquit; Qui ita affectus, beatum esse numquam probabis; Quid igitur, inquit, eos responsuros putas? Ergo opifex plus sibi proponet ad formarum quam civis excellens ad factorum pulchritudinem? Pauca mutat vel plura sane; At Zeno eum non beatum modo, sed etiam divitem dicere ausus est. Nobis aliter videtur, recte secusne, postea; Bestiarum vero nullum iudicium puto. At quicum ioca seria, ut dicitur, quicum arcana, quicum occulta omnia?

+ + + +

Immo alio genere; Rationis enim perfectio est virtus; Eam si varietatem diceres, intellegerem, ut etiam non dicente te intellego; Nos cum te, M. Inde igitur, inquit, ordiendum est. Hoc tu nunc in illo probas. Dat enim intervalla et relaxat. At, illa, ut vobis placet, partem quandam tuetur, reliquam deserit.

+ + + +

Maximus dolor, inquit, brevis est.

+ + + +

Ergo, inquit, tibi Q. Nescio quo modo praetervolavit oratio. Quid de Pythagora? Quid igitur dubitamus in tota eius natura quaerere quid sit effectum? Quae quo sunt excelsiores, eo dant clariora indicia naturae. Beatus sibi videtur esse moriens. Aliter enim explicari, quod quaeritur, non potest. Iam enim adesse poterit.

+ + + +

Aliter enim explicari, quod quaeritur, non potest. Eadem nunc mea adversum te oratio est. Te enim iudicem aequum puto, modo quae dicat ille bene noris. Respondent extrema primis, media utrisque, omnia omnibus. Bona autem corporis huic sunt, quod posterius posui, similiora. Quae si potest singula consolando levare, universa quo modo sustinebit?

+ + + +
    +
  • Nisi enim id faceret, cur Plato Aegyptum peragravit, ut a sacerdotibus barbaris numeros et caelestia acciperet?
  • + + + +
  • Primum quid tu dicis breve?
  • + + + +
  • Quid enim me prohiberet Epicureum esse, si probarem, quae ille diceret?
  • + + + +
  • Conferam tecum, quam cuique verso rem subicias;
  • +
+ + + +

Teneo, inquit, finem illi videri nihil dolere. Quasi vero aut concedatur in omnibus stultis aeque magna esse vitia, et eadem inbecillitate et inconstantia L. At ego quem huic anteponam non audeo dicere; Nam Pyrrho, Aristo, Erillus iam diu abiecti. Quos quidem tibi studiose et diligenter tractandos magnopere censeo. Sed emolumenta communia esse dicuntur, recte autem facta et peccata non habentur communia. Qui autem esse poteris, nisi te amor ipse ceperit? Res enim se praeclare habebat, et quidem in utraque parte.

+ + + +

Qui autem de summo bono dissentit de tota philosophiae ratione dissentit.

+ + + +

In his igitur partibus duabus nihil erat, quod Zeno commutare gestiret. Scaevola tribunus plebis ferret ad plebem vellentne de ea re quaeri. Qui-vere falsone, quaerere mittimus-dicitur oculis se privasse; Res enim se praeclare habebat, et quidem in utraque parte. Qua tu etiam inprudens utebare non numquam. Tanti autem aderant vesicae et torminum morbi, ut nihil ad eorum magnitudinem posset accedere. Primum in nostrane potestate est, quid meminerimus?

+ + + +

His enim rebus detractis negat se reperire in asotorum vita quod reprehendat. At, si voluptas esset bonum, desideraret. Sint modo partes vitae beatae. Tecum optime, deinde etiam cum mediocri amico. Satisne vobis videor pro meo iure in vestris auribus commentatus?

+ + + +

Vestri haec verecundius, illi fortasse constantius. Reguli reiciendam; Ita ne hoc quidem modo paria peccata sunt. Nunc omni virtuti vitium contrario nomine opponitur. Eorum enim est haec querela, qui sibi cari sunt seseque diligunt. Est tamen ea secundum naturam multoque nos ad se expetendam magis hortatur quam superiora omnia.

+ + + +

Torquatus, is qui consul cum Cn. Tum ille: Tu autem cum ipse tantum librorum habeas, quos hic tandem requiris? Sed ea mala virtuti magnitudine obruebantur. Non autem hoc: igitur ne illud quidem. Vidit Homerus probari fabulam non posse, si cantiunculis tantus irretitus vir teneretur; Roges enim Aristonem, bonane ei videantur haec: vacuitas doloris, divitiae, valitudo; Est, ut dicis, inquam. Beatus sibi videtur esse moriens. Quorum altera prosunt, nocent altera. Ita graviter et severe voluptatem secrevit a bono. Quid ei reliquisti, nisi te, quoquo modo loqueretur, intellegere, quid diceret? Non quam nostram quidem, inquit Pomponius iocans;

+ + + +

Cum id fugiunt, re eadem defendunt, quae Peripatetici, verba. Ita multa dicunt, quae vix intellegam. Si longus, levis dictata sunt. Si enim ita est, vide ne facinus facias, cum mori suadeas. Conferam tecum, quam cuique verso rem subicias; Si qua in iis corrigere voluit, deteriora fecit. Ita multo sanguine profuso in laetitia et in victoria est mortuus. Nulla profecto est, quin suam vim retineat a primo ad extremum. Haec quo modo conveniant, non sane intellego.

+ + + +
    +
  • Ex eorum enim scriptis et institutis cum omnis doctrina liberalis, omnis historia.
  • + + + +
  • Tum Piso: Quoniam igitur aliquid omnes, quid Lucius noster?
  • + + + +
  • Quae dici eadem de ceteris virtutibus possunt, quarum omnium fundamenta vos in voluptate tamquam in aqua ponitis.
  • + + + +
  • Quae est igitur causa istarum angustiarum?
  • +
+ + + +
+

+ Quaerimus enim finem bonorum. +

+
+ + + +
+

+ Cuius similitudine perspecta in formarum specie ac dignitate transitum est ad honestatem dictorum atque factorum. +

+
+ + + +
+

+ Illi igitur antiqui non tam acute optabiliorem illam vitam putant, praestantiorem, beatiorem, Stoici autem tantum modo praeponendam in seligendo, non quo beatior ea vita sit, sed quod ad naturam accommodatior. +

+
+ + + +
+ + + +

Tum Torquatus: Prorsus, inquit, assentior; Sed fortuna fortis; Magni enim aestimabat pecuniam non modo non contra leges, sed etiam legibus partam. Haec para/doca illi, nos admirabilia dicamus. Eadem nunc mea adversum te oratio est. Atque hoc loco similitudines eas, quibus illi uti solent, dissimillimas proferebas.

+ + + +

Omnes enim iucundum motum, quo sensus hilaretur. Quam ob rem tandem, inquit, non satisfacit? Quid enim de amicitia statueris utilitatis causa expetenda vides. Idem iste, inquam, de voluptate quid sentit? Nam, ut sint illa vendibiliora, haec uberiora certe sunt. Ut placet, inquit, etsi enim illud erat aptius, aequum cuique concedere.

+ + + +

Quam ob rem tandem, inquit, non satisfacit? Itaque si aut requietem natura non quaereret aut eam posset alia quadam ratione consequi. Primum cur ista res digna odio est, nisi quod est turpis? Quodsi ipsam honestatem undique pertectam atque absolutam.

+ + + +
    +
  • Non enim, si omnia non sequebatur, idcirco non erat ortus illinc.
  • + + + +
  • Respondent extrema primis, media utrisque, omnia omnibus.
  • + + + +
  • Ergo instituto veterum, quo etiam Stoici utuntur, hinc capiamus exordium.
  • + + + +
  • Mene ergo et Triarium dignos existimas, apud quos turpiter loquare?
  • + + + +
  • Atque etiam ad iustitiam colendam, ad tuendas amicitias et reliquas caritates quid natura valeat haec una cognitio potest tradere.
  • +
+ + + +

Si stante, hoc natura videlicet vult, salvam esse se, quod concedimus; Cave putes quicquam esse verius. Sed haec in pueris; Sed hoc sane concedamus. Terram, mihi crede, ea lanx et maria deprimet. Sint modo partes vitae beatae. Ego vero volo in virtute vim esse quam maximam; Si quicquam extra virtutem habeatur in bonis. Est enim effectrix multarum et magnarum voluptatum. In quibus doctissimi illi veteres inesse quiddam caeleste et divinum putaverunt.

+ + + +

Ipse Epicurus fortasse redderet, ut Sextus Peducaeus, Sex. Sed haec quidem liberius ab eo dicuntur et saepius. Itaque sensibus rationem adiunxit et ratione effecta sensus non reliquit. Mihi, inquam, qui te id ipsum rogavi? Uterque enim summo bono fruitur, id est voluptate. Cui Tubuli nomen odio non est?

+ + + +

Beatus sibi videtur esse moriens. Nos cum te, M. Qua igitur re ab deo vincitur, si aeternitate non vincitur? Primum in nostrane potestate est, quid meminerimus? A mene tu? Illa tamen simplicia, vestra versuta. Neque enim disputari sine reprehensione nec cum iracundia aut pertinacia recte disputari potest. Quam ob rem tandem, inquit, non satisfacit?

+ + + +

Nemo igitur esse beatus potest. Cui Tubuli nomen odio non est? Sed residamus, inquit, si placet. Illa argumenta propria videamus, cur omnia sint paria peccata. Quae cum praeponunt, ut sit aliqua rerum selectio, naturam videntur sequi; Dic in quovis conventu te omnia facere, ne doleas. Quo modo autem philosophus loquitur? Qui autem de summo bono dissentit de tota philosophiae ratione dissentit.

+ + + +

Pugnant Stoici cum Peripateticis. Non laboro, inquit, de nomine. Tecum optime, deinde etiam cum mediocri amico. Quae cum essent dicta, discessimus. Ita cum ea volunt retinere, quae superiori sententiae conveniunt, in Aristonem incidunt; Id mihi magnum videtur.

+ + + +

Nummus in Croesi divitiis obscuratur, pars est tamen divitiarum.

+ + + +

Tum, Quintus et Pomponius cum idem se velle dixissent, Piso exorsus est. Quae duo sunt, unum facit. Paria sunt igitur. Et quod est munus, quod opus sapientiae? Bonum integritas corporis: misera debilitas. Nonne videmus quanta perturbatio rerum omnium consequatur, quanta confusio? Bonum integritas corporis: misera debilitas. Quamquam ab iis philosophiam et omnes ingenuas disciplinas habemus; Sed tamen est aliquid, quod nobis non liceat, liceat illis.

+ + + +

Respondent extrema primis, media utrisque, omnia omnibus. Hoc etsi multimodis reprehendi potest, tamen accipio, quod dant. Quaesita enim virtus est, non quae relinqueret naturam, sed quae tueretur. Videamus animi partes, quarum est conspectus illustrior; Callipho ad virtutem nihil adiunxit nisi voluptatem, Diodorus vacuitatem doloris. Verba tu fingas et ea dicas, quae non sentias?

+ + + +
    +
  • Utrum igitur tibi litteram videor an totas paginas commovere?
  • + + + +
  • Expressa vero in iis aetatibus, quae iam confirmatae sunt.
  • + + + +
  • Cuius etiam illi hortuli propinqui non memoriam solum mihi afferunt, sed ipsum videntur in conspectu meo ponere.
  • + + + +
  • Plane idem, inquit, et maxima quidem, qua fieri nulla maior potest.
  • +
+ + + +

Mihi enim satis est, ipsis non satis. Atqui, inquam, Cato, si istud optinueris, traducas me ad te totum licebit. An hoc usque quaque, aliter in vita? Sed quod proximum fuit non vidit.

+ + + +
    +
  • Tu vero, inquam, ducas licet, si sequetur;
  • + + + +
  • Quid iudicant sensus?
  • + + + +
  • Etenim nec iustitia nec amicitia esse omnino poterunt, nisi ipsae per se expetuntur.
  • + + + +
  • Si enim ad populum me vocas, eum.
  • + + + +
  • Nec tamen ille erat sapiens quis enim hoc aut quando aut ubi aut unde?
  • +
+ + + +

Id Sextilius factum negabat. Philosophi autem in suis lectulis plerumque moriuntur.

+ + + +
Rhetorice igitur, inquam, nos mavis quam dialectice disputare?
+ + + +

Quamquam tu hanc copiosiorem etiam soles dicere. De hominibus dici non necesse est. Quid ergo attinet dicere: Nihil haberem, quod reprehenderem, si finitas cupiditates haberent? Et ille ridens: Video, inquit, quid agas; Collatio igitur ista te nihil iuvat. Sin aliud quid voles, postea.

+ + + +
    +
  • Ergo adhuc, quantum equidem intellego, causa non videtur fuisse mutandi nominis.
  • + + + +
  • Profectus in exilium Tubulus statim nec respondere ausus;
  • + + + +
  • Legimus tamen Diogenem, Antipatrum, Mnesarchum, Panaetium, multos alios in primisque familiarem nostrum Posidonium.
  • + + + +
  • Verum tamen cum de rebus grandioribus dicas, ipsae res verba rapiunt;
  • +
+ + + +

Ut in voluptate sit, qui epuletur, in dolore, qui torqueatur. Quis negat? Hi autem ponunt illi quidem prima naturae, sed ea seiungunt a finibus et a summa bonorum; Haec para/doca illi, nos admirabilia dicamus. Quod autem magnum dolorem brevem, longinquum levem esse dicitis, id non intellego quale sit. Istam voluptatem perpetuam quis potest praestare sapienti? Cur post Tarentum ad Archytam? Huic mori optimum esse propter desperationem sapientiae, illi propter spem vivere. Apud imperitos tum illa dicta sunt, aliquid etiam coronae datum; Nos commodius agimus.

+ + + +
    +
  • In qua quid est boni praeter summam voluptatem, et eam sempiternam?
  • + + + +
  • Sin te auctoritas commovebat, nobisne omnibus et Platoni ipsi nescio quem illum anteponebas?
  • + + + +
  • Sed potestne rerum maior esse dissensio?
  • + + + +
  • Stoici scilicet.
  • + + + +
  • Cupit enim dícere nihil posse ad beatam vitam deesse sapienti.
  • +
+ + + +

Sequitur disserendi ratio cognitioque naturae;

+ + + +

Hos contra singulos dici est melius. An ea, quae per vinitorem antea consequebatur, per se ipsa curabit? Videmus igitur ut conquiescere ne infantes quidem possint. Sapiens autem semper beatus est et est aliquando in dolore; Duo enim genera quae erant, fecit tria. Inde igitur, inquit, ordiendum est.

+ + + +

Itaque hic ipse iam pridem est reiectus; Hoc loco tenere se Triarius non potuit. Sed ad illum redeo. Quae duo sunt, unum facit. Nihil acciderat ei, quod nollet, nisi quod anulum, quo delectabatur, in mari abiecerat.

+ + + +
Habent enim et bene longam et satis litigiosam disputationem.
+ + + +

At, si voluptas esset bonum, desideraret. Maximus dolor, inquit, brevis est.

+ + + +
    +
  • Compensabatur, inquit, cum summis doloribus laetitia.
  • + + + +
  • Tu vero, inquam, ducas licet, si sequetur;
  • + + + +
  • Addidisti ad extremum etiam indoctum fuisse.
  • + + + +
  • Deinde disputat, quod cuiusque generis animantium statui deceat extremum.
  • +
+ + + +

Qui autem esse poteris, nisi te amor ipse ceperit? Si stante, hoc natura videlicet vult, salvam esse se, quod concedimus; An tu me de L. Si verbum sequimur, primum longius verbum praepositum quam bonum. Deinde disputat, quod cuiusque generis animantium statui deceat extremum. Ostendit pedes et pectus. Sed haec quidem liberius ab eo dicuntur et saepius. Nam, ut paulo ante docui, augendae voluptatis finis est doloris omnis amotio. Neque enim civitas in seditione beata esse potest nec in discordia dominorum domus;

+ + + +
+

+ Mihi, inquam, qui te id ipsum rogavi? +

+
+ + + +
+

+ Restant Stoici, qui cum a Peripateticis et Academicis omnia transtulissent, nominibus aliis easdem res secuti sunt. +

+
+ + + +

Et hunc idem dico, inquieta sed ad virtutes et ad vitia nihil interesse.

+ + + +

Quo modo autem optimum, si bonum praeterea nullum est? Ab hoc autem quaedam non melius quam veteres, quaedam omnino relicta. Aperiendum est igitur, quid sit voluptas; Quid autem habent admirationis, cum prope accesseris? Huius ego nunc auctoritatem sequens idem faciam. Videsne, ut haec concinant? Deinde disputat, quod cuiusque generis animantium statui deceat extremum.

+ + + +

Et hunc idem dico, inquieta sed ad virtutes et ad vitia nihil interesse. Num igitur utiliorem tibi hunc Triarium putas esse posse, quam si tua sint Puteolis granaria? Sed haec ab Antiocho, familiari nostro, dicuntur multo melius et fortius, quam a Stasea dicebantur. Illud quaero, quid ei, qui in voluptate summum bonum ponat, consentaneum sit dicere. Nullum inveniri verbum potest quod magis idem declaret Latine, quod Graece, quam declarat voluptas. An me, inquam, nisi te audire vellem, censes haec dicturum fuisse?

+ + + +

Nunc haec primum fortasse audientis servire debemus. Honesta oratio, Socratica, Platonis etiam.

+ + + +

Nihil enim iam habes, quod ad corpus referas; Atqui reperies, inquit, in hoc quidem pertinacem; Summus dolor plures dies manere non potest? At ille pellit, qui permulcet sensum voluptate. Quis istud possit, inquit, negare?

+ + + +
At iste non dolendi status non vocatur voluptas.
+ + + +

Invidiosum nomen est, infame, suspectum. Quae cum dixisset paulumque institisset, Quid est? Quae fere omnia appellantur uno ingenii nomine, easque virtutes qui habent, ingeniosi vocantur. Nihil enim iam habes, quod ad corpus referas; Nihil opus est exemplis hoc facere longius. Atque haec ita iustitiae propria sunt, ut sint virtutum reliquarum communia. Atqui reperies, inquit, in hoc quidem pertinacem;

+ + + +

Sed ad haec, nisi molestum est, habeo quae velim. Age, inquies, ista parva sunt. Quasi ego id curem, quid ille aiat aut neget. Et hunc idem dico, inquieta sed ad virtutes et ad vitia nihil interesse. Nos cum te, M. Ut id aliis narrare gestiant?

+ + + +

Septem autem illi non suo, sed populorum suffragio omnium nominati sunt. Videmus igitur ut conquiescere ne infantes quidem possint. Rationis enim perfectio est virtus; Illa tamen simplicia, vestra versuta. Nihil enim iam habes, quod ad corpus referas; Quae animi affectio suum cuique tribuens atque hanc, quam dico. Item de contrariis, a quibus ad genera formasque generum venerunt. Illum mallem levares, quo optimum atque humanissimum virum, Cn. At hoc in eo M. Tum mihi Piso: Quid ergo? Respondeat totidem verbis.

+ + + +

Aliter homines, aliter philosophos loqui putas oportere? Nam quibus rebus efficiuntur voluptates, eae non sunt in potestate sapientis. Si longus, levis dictata sunt. Itaque eos id agere, ut a se dolores, morbos, debilitates repellant. Quid, si non sensus modo ei sit datus, verum etiam animus hominis? Nihil opus est exemplis hoc facere longius. Non minor, inquit, voluptas percipitur ex vilissimis rebus quam ex pretiosissimis. Poterat autem inpune;

+ + + +
Tamen a proposito, inquam, aberramus.
+ + + +

Quod quidem nobis non saepe contingit. Quae cum magnifice primo dici viderentur, considerata minus probabantur. Quas enim kakaw Graeci appellant, vitia malo quam malitias nominare. Quodcumque in mentem incideret, et quodcumque tamquam occurreret. At certe gravius.

+ + + +
Eam tum adesse, cum dolor omnis absit;
+ + + +

Quae similitudo in genere etiam humano apparet. Sed quoniam et advesperascit et mihi ad villam revertendum est, nunc quidem hactenus; Ita multo sanguine profuso in laetitia et in victoria est mortuus. Atque hoc loco similitudines eas, quibus illi uti solent, dissimillimas proferebas. Sed tamen enitar et, si minus multa mihi occurrent, non fugiam ista popularia. Hoc mihi cum tuo fratre convenit. Quo modo autem philosophus loquitur? Vives, inquit Aristo, magnifice atque praeclare, quod erit cumque visum ages, numquam angere, numquam cupies, numquam timebis. Pauca mutat vel plura sane;

+ + + +

His enim rebus detractis negat se reperire in asotorum vita quod reprehendat. Age sane, inquam. Ratio enim nostra consentit, pugnat oratio. Ait enim se, si uratur, Quam hoc suave! dicturum. His enim rebus detractis negat se reperire in asotorum vita quod reprehendat. Is es profecto tu. Dempta enim aeternitate nihilo beatior Iuppiter quam Epicurus; At miser, si in flagitiosa et vitiosa vita afflueret voluptatibus.

+ + + +

Idque testamento cavebit is, qui nobis quasi oraculum ediderit nihil post mortem ad nos pertinere?

+ + + +

Nam, ut sint illa vendibiliora, haec uberiora certe sunt. Septem autem illi non suo, sed populorum suffragio omnium nominati sunt. Etenim semper illud extra est, quod arte comprehenditur. An est aliquid, quod te sua sponte delectet? Solum praeterea formosum, solum liberum, solum civem, stultost; Quis istum dolorem timet?

+ + + +

Compensabatur, inquit, cum summis doloribus laetitia. Duarum enim vitarum nobis erunt instituta capienda. Beatus autem esse in maximarum rerum timore nemo potest. Ut alios omittam, hunc appello, quem ille unum secutus est.

+ + + +
    +
  • Quid, quod homines infima fortuna, nulla spe rerum gerendarum, opifices denique delectantur historia?
  • + + + +
  • Illo enim addito iuste fit recte factum, per se autem hoc ipsum reddere in officio ponitur.
  • + + + +
  • Quo igitur, inquit, modo?
  • + + + +
  • Sed est forma eius disciplinae, sicut fere ceterarum, triplex: una pars est naturae, disserendi altera, vivendi tertia.
  • +
+ + + +

Num igitur dubium est, quin, si in re ipsa nihil peccatur a superioribus, verbis illi commodius utantur? Nihil sane. Si mala non sunt, iacet omnis ratio Peripateticorum. Tum mihi Piso: Quid ergo? Amicitiam autem adhibendam esse censent, quia sit ex eo genere, quae prosunt. Deinde disputat, quod cuiusque generis animantium statui deceat extremum. Atqui reperies, inquit, in hoc quidem pertinacem; Quid paulo ante, inquit, dixerim nonne meministi, cum omnis dolor detractus esset, variari, non augeri voluptatem? Tum Lucius: Mihi vero ista valde probata sunt, quod item fratri puto. Hic nihil fuit, quod quaereremus. Ratio quidem vestra sic cogit. Mihi enim satis est, ipsis non satis.

+ + + +
    +
  • Nam et a te perfici istam disputationem volo, nec tua mihi oratio longa videri potest.
  • + + + +
  • Audax negotium, dicerem impudens, nisi hoc institutum postea translatum ad philosophos nostros esset.
  • + + + +
  • Sed in rebus apertissimis nimium longi sumus.
  • + + + +
  • Itaque dicunt nec dubitant: mihi sic usus est, tibi ut opus est facto, fac.
  • + + + +
  • Illi enim inter se dissentiunt.
  • + + + +
  • Sed haec omittamus;
  • +
+ + + +
    +
  • Magni enim aestimabat pecuniam non modo non contra leges, sed etiam legibus partam.
  • + + + +
  • Nunc haec primum fortasse audientis servire debemus.
  • + + + +
  • Quodsi vultum tibi, si incessum fingeres, quo gravior viderere, non esses tui similis;
  • +
+ + + +

Est enim effectrix multarum et magnarum voluptatum.

+ + + +
+ + + +

Quod ea non occurrentia fingunt, vincunt Aristonem; Negat esse eam, inquit, propter se expetendam. Hoc tu nunc in illo probas. Parvi enim primo ortu sic iacent, tamquam omnino sine animo sint. Quid enim de amicitia statueris utilitatis causa expetenda vides. Ergo illi intellegunt quid Epicurus dicat, ego non intellego? Quae qui non vident, nihil umquam magnum ac cognitione dignum amaverunt. Eiuro, inquit adridens, iniquum, hac quidem de re; Sic enim censent, oportunitatis esse beate vivere. An vero displicuit ea, quae tributa est animi virtutibus tanta praestantia?

+ + + +

Mihi quidem Homerus huius modi quiddam vidisse videatur in iis, quae de Sirenum cantibus finxerit. Sic consequentibus vestris sublatis prima tolluntur. Summus dolor plures dies manere non potest? Egone non intellego, quid sit don Graece, Latine voluptas?

+ + + +
+

+ Nec enim absolvi beata vita sapientis neque ad exitum perduci poterit, si prima quaeque bene ab eo consulta atque facta ipsius oblivione obruentur. +

+
+ + + +

Traditur, inquit, ab Epicuro ratio neglegendi doloris. Huius, Lyco, oratione locuples, rebus ipsis ielunior. Si longus, levis; Mene ergo et Triarium dignos existimas, apud quos turpiter loquare? Ut placet, inquit, etsi enim illud erat aptius, aequum cuique concedere. Eadem fortitudinis ratio reperietur. Si autem id non concedatur, non continuo vita beata tollitur. Equidem, sed audistine modo de Carneade? Tubulo putas dicere? Qui autem de summo bono dissentit de tota philosophiae ratione dissentit.

+ + + +

Quo plebiscito decreta a senatu est consuli quaestio Cn. Utinam quidem dicerent alium alio beatiorem! Iam ruinas videres. Quare si potest esse beatus is, qui est in asperis reiciendisque rebus, potest is quoque esse. Ego vero isti, inquam, permitto. Consequens enim est et post oritur, ut dixi. Sed nimis multa. Videsne, ut haec concinant? Illa argumenta propria videamus, cur omnia sint paria peccata. Egone non intellego, quid sit don Graece, Latine voluptas? At coluit ipse amicitias.

+ + + +

Re mihi non aeque satisfacit, et quidem locis pluribus.

+ + + +

Qui autem de summo bono dissentit de tota philosophiae ratione dissentit. Sed videbimus. Expressa vero in iis aetatibus, quae iam confirmatae sunt. Hoc positum in Phaedro a Platone probavit Epicurus sensitque in omni disputatione id fieri oportere. Itaque his sapiens semper vacabit. Quae est igitur causa istarum angustiarum? Eam stabilem appellas.

+ + + +

Mihi enim satis est, ipsis non satis. Tum mihi Piso: Quid ergo? Atqui reperies, inquit, in hoc quidem pertinacem; Stoici scilicet. Tum mihi Piso: Quid ergo? Equidem e Cn.

+ + + +
Facillimum id quidem est, inquam.
+ + + +

Potius inflammat, ut coercendi magis quam dedocendi esse videantur. Respondeat totidem verbis. Ex rebus enim timiditas, non ex vocabulis nascitur. Non pugnem cum homine, cur tantum habeat in natura boni; Prodest, inquit, mihi eo esse animo. Paria sunt igitur. Aliter enim nosmet ipsos nosse non possumus. Bonum incolumis acies: misera caecitas. Sed quae tandem ista ratio est? Similiter sensus, cum accessit ad naturam, tuetur illam quidem, sed etiam se tuetur;

+ + + +

Similiter sensus, cum accessit ad naturam, tuetur illam quidem, sed etiam se tuetur;

+ + + +

Est autem etiam actio quaedam corporis, quae motus et status naturae congruentis tenet; Erit enim mecum, si tecum erit. Ut optime, secundum naturam affectum esse possit. Quis istud, quaeso, nesciebat? Ita nemo beato beatior. At ille pellit, qui permulcet sensum voluptate. Ille incendat? De hominibus dici non necesse est. Dolor ergo, id est summum malum, metuetur semper, etiamsi non aderit;

+ + + +

Quid, si etiam iucunda memoria est praeteritorum malorum?

+ + + +

Hoc enim constituto in philosophia constituta sunt omnia. Nunc omni virtuti vitium contrario nomine opponitur. Illum mallem levares, quo optimum atque humanissimum virum, Cn. Ita ne hoc quidem modo paria peccata sunt. Si quae forte-possumus. Egone quaeris, inquit, quid sentiam? Piso, familiaris noster, et alia multa et hoc loco Stoicos irridebat: Quid enim? Laelius clamores sofòw ille so lebat Edere compellans gumias ex ordine nostros.

+ + + +
    +
  • Sed ad haec, nisi molestum est, habeo quae velim.
  • + + + +
  • Quod cum ille dixisset et satis disputatum videretur, in oppidum ad Pomponium perreximus omnes.
  • + + + +
  • Nam neque virtute retinetur ille in vita, nec iis, qui sine virtute sunt, mors est oppetenda.
  • + + + +
  • Sed emolumenta communia esse dicuntur, recte autem facta et peccata non habentur communia.
  • +
+ + + +
    +
  • Esse enim quam vellet iniquus iustus poterat inpune.
  • + + + +
  • Quod cum accidisset ut alter alterum necopinato videremus, surrexit statim.
  • + + + +
  • Quae duo sunt, unum facit.
  • + + + +
  • Nunc ita separantur, ut disiuncta sint, quo nihil potest esse perversius.
  • + + + +
  • Quod dicit Epicurus etiam de voluptate, quae minime sint voluptates, eas obscurari saepe et obrui.
  • + + + +
  • Tantum dico, magis fuisse vestrum agere Epicuri diem natalem, quam illius testamento cavere ut ageretur.
  • +
+ + + +

Itaque fecimus. Non dolere, inquam, istud quam vim habeat postea videro; Et quidem, inquit, vehementer errat; Bonum valitudo: miser morbus. Sed ad rem redeamus; Suo enim quisque studio maxime ducitur. An ea, quae per vinitorem antea consequebatur, per se ipsa curabit? Sed nimis multa.

+ + + +

Qualem igitur hominem natura inchoavit?

+ + + +
+ + + +

Suo enim quisque studio maxime ducitur. Si enim, ut mihi quidem videtur, non explet bona naturae voluptas, iure praetermissa est; Praeclare hoc quidem. Theophrastus mediocriterne delectat, cum tractat locos ab Aristotele ante tractatos? Idem etiam dolorem saepe perpetiuntur, ne, si id non faciant, incidant in maiorem. Si longus, levis;

+ + + +

Ut enim consuetudo loquitur, id solum dicitur honestum, quod est populari fama gloriosum. Sed finge non solum callidum eum, qui aliquid improbe faciat, verum etiam praepotentem, ut M. Multoque hoc melius nos veriusque quam Stoici. Ut nemo dubitet, eorum omnia officia quo spectare, quid sequi, quid fugere debeant? Sit enim idem caecus, debilis. Tum Piso: Quoniam igitur aliquid omnes, quid Lucius noster? Qua tu etiam inprudens utebare non numquam.

+ + + +
Quia dolori non voluptas contraria est, sed doloris privatio.
+ + + +

At iam decimum annum in spelunca iacet. Bonum liberi: misera orbitas.

+ + + +

Indicant pueri, in quibus ut in speculis natura cernitur. -, sed ut hoc iudicaremus, non esse in iis partem maximam positam beate aut secus vivendi. Itaque his sapiens semper vacabit. Quo plebiscito decreta a senatu est consuli quaestio Cn.

+ + + +

Velut ego nunc moveor.

+ + + +

Cur haec eadem Democritus? Ergo ita: non posse honeste vivi, nisi honeste vivatur? Ergo in utroque exercebantur, eaque disciplina effecit tantam illorum utroque in genere dicendi copiam. Idemne, quod iucunde? Et quod est munus, quod opus sapientiae?

+ + + +
    +
  • Hoc Hieronymus summum bonum esse dixit.
  • + + + +
  • De malis autem et bonis ab iis animalibus, quae nondum depravata sint, ait optime iudicari.
  • + + + +
  • Ex eorum enim scriptis et institutis cum omnis doctrina liberalis, omnis historia.
  • + + + +
  • Hoc mihi cum tuo fratre convenit.
  • + + + +
  • Negat enim summo bono afferre incrementum diem.
  • + + + +
  • Omnia contraria, quos etiam insanos esse vultis.
  • +
+ + + +
+

+ Ex quo intellegitur officium medium quiddam esse, quod neque in bonis ponatur neque in contrariis. +

+
+ + + +

Dat enim intervalla et relaxat. Tu enim ista lenius, hic Stoicorum more nos vexat. Hoc non est positum in nostra actione. Qui non moveatur et offensione turpitudinis et comprobatione honestatis? Contineo me ab exemplis. Si mala non sunt, iacet omnis ratio Peripateticorum. Nam quid possumus facere melius? Quod autem ratione actum est, id officium appellamus.

+ + + +
Nunc omni virtuti vitium contrario nomine opponitur.
+ + + +

Nam quid possumus facere melius? At multis se probavit. Illud non continuo, ut aeque incontentae. Oratio me istius philosophi non offendit; Quae in controversiam veniunt, de iis, si placet, disseramus. Haec bene dicuntur, nec ego repugno, sed inter sese ipsa pugnant.

+ + + +
+

+ Quam ob rem turpe putandum est, non dico dolere-nam id quidem est interdum necesse-, sed saxum illud Lemnium clamore Philocteteo funestare, Quod eiulatu, questu, gemitu, fremitibus Resonando mutum flebiles voces refert. +

+
+ + + +
+

+ Et quae per vim oblatum stuprum volontaria morte lueret inventa est et qui interficeret filiam, ne stupraretur. +

+
+ + + +

Non est ista, inquam, Piso, magna dissensio. Te autem hortamur omnes, currentem quidem, ut spero, ut eos, quos novisse vis, imitari etiam velis. Nam adhuc, meo fortasse vitio, quid ego quaeram non perspicis. Aliter homines, aliter philosophos loqui putas oportere? Ita multo sanguine profuso in laetitia et in victoria est mortuus. Primum in nostrane potestate est, quid meminerimus? Moriatur, inquit. Non enim, si malum est dolor, carere eo malo satis est ad bene vivendum. Verba tu fingas et ea dicas, quae non sentias? Sic, et quidem diligentius saepiusque ista loquemur inter nos agemusque communiter. Sed fortuna fortis; Quae quidem sapientes sequuntur duce natura tamquam videntes;

+ + + +

Quonam modo? Itaque eos id agere, ut a se dolores, morbos, debilitates repellant. Nam Pyrrho, Aristo, Erillus iam diu abiecti. Est, ut dicis, inquam. Ne in odium veniam, si amicum destitero tueri. Bonum incolumis acies: misera caecitas.

+ + + +

Vos autem cum perspicuis dubia debeatis illustrare, dubiis perspicua conamini tollere. Vulgo enim dicitur: Iucundi acti labores, nec male Euripidesconcludam, si potero, Latine; Isto modo ne improbos quidem, si essent boni viri. Atque haec ita iustitiae propria sunt, ut sint virtutum reliquarum communia. Mihi, inquam, qui te id ipsum rogavi?

+ + + +
Expressa vero in iis aetatibus, quae iam confirmatae sunt.
+ + + +

Cum ageremus, inquit, vitae beatum et eundem supremum diem, scribebamus haec. Sunt enim quasi prima elementa naturae, quibus ubertas orationis adhiberi vix potest, nec equidem eam cogito consectari. Atque his de rebus et splendida est eorum et illustris oratio. Tamen aberramus a proposito, et, ne longius, prorsus, inquam, Piso, si ista mala sunt, placet. Istic sum, inquit. Quid affers, cur Thorius, cur Caius Postumius, cur omnium horum magister, Orata, non iucundissime vixerit? Haec dicuntur inconstantissime. Quis est tam dissimile homini. Non autem hoc: igitur ne illud quidem. Atqui eorum nihil est eius generis, ut sit in fine atque extrerno bonorum.

+ + + +

Ut id aliis narrare gestiant? Octavio fuit, cum illam severitatem in eo filio adhibuit, quem in adoptionem D. Quod non faceret, si in voluptate summum bonum poneret.

+ + + +

Nam si beatus umquam fuisset, beatam vitam usque ad illum a Cyro extructum rogum pertulisset. Si est nihil nisi corpus, summa erunt illa: valitudo, vacuitas doloris, pulchritudo, cetera. Num igitur utiliorem tibi hunc Triarium putas esse posse, quam si tua sint Puteolis granaria? Unum est sine dolore esse, alterum cum voluptate. Quid affers, cur Thorius, cur Caius Postumius, cur omnium horum magister, Orata, non iucundissime vixerit? Cur id non ita fit? Tanti autem aderant vesicae et torminum morbi, ut nihil ad eorum magnitudinem posset accedere.

+ + + +

Non autem hoc: igitur ne illud quidem. Qui non moveatur et offensione turpitudinis et comprobatione honestatis? Dici enim nihil potest verius. At miser, si in flagitiosa et vitiosa vita afflueret voluptatibus. Tum ille: Ain tandem? Simus igitur contenti his. Easdemne res? Sine ea igitur iucunde negat posse se vivere?

+ + + +

Et quidem, inquit, vehementer errat; Quid enim necesse est, tamquam meretricem in matronarum coetum, sic voluptatem in virtutum concilium adducere? Quid autem habent admirationis, cum prope accesseris? Res enim fortasse verae, certe graves, non ita tractantur, ut debent, sed aliquanto minutius. Non minor, inquit, voluptas percipitur ex vilissimis rebus quam ex pretiosissimis. Quod cum dixissent, ille contra.

+ + + +
+

+ Ipse negat, ut ante dixi, luxuriosorum vitam reprehendendam, nisi plane fatui sint, id est nisi aut cupiant aut metuant. +

+
+ + + +
Equidem, sed audistine modo de Carneade?
+ + + +

Unum nescio, quo modo possit, si luxuriosus sit, finitas cupiditates habere. Egone quaeris, inquit, quid sentiam? Sed residamus, inquit, si placet. Quae cum essent dicta, discessimus. An vero, inquit, quisquam potest probare, quod perceptfum, quod. At hoc in eo M. Bonum incolumis acies: misera caecitas. Cur id non ita fit?

+ + + +

Scrupulum, inquam, abeunti; Quippe: habes enim a rhetoribus; Aliter enim nosmet ipsos nosse non possumus. Quasi vero, inquit, perpetua oratio rhetorum solum, non etiam philosophorum sit. An hoc usque quaque, aliter in vita? Non dolere, inquam, istud quam vim habeat postea videro; Itaque fecimus. Quamquam non negatis nos intellegere quid sit voluptas, sed quid ille dicat.

+ + + +
Homines optimi non intellegunt totam rationem everti, si ita res se habeat.
+ + + +

Quippe: habes enim a rhetoribus; Vitiosum est enim in dividendo partem in genere numerare. Omnia contraria, quos etiam insanos esse vultis. Compensabatur, inquit, cum summis doloribus laetitia. Quod cum dixissent, ille contra. Dolere malum est: in crucem qui agitur, beatus esse non potest. Quis istud possit, inquit, negare?

+ + + +

Quid de Platone aut de Democrito loquar? Tria genera bonorum; Tum Piso: Quoniam igitur aliquid omnes, quid Lucius noster? Equidem soleo etiam quod uno Graeci, si aliter non possum, idem pluribus verbis exponere. Est, ut dicis, inquit; Post enim Chrysippum eum non sane est disputatum.

+ + + +
Negabat igitur ullam esse artem, quae ipsa a se proficisceretur;
+ + + +

Mihi quidem Antiochum, quem audis, satis belle videris attendere. Haec igitur Epicuri non probo, inquam. Ut nemo dubitet, eorum omnia officia quo spectare, quid sequi, quid fugere debeant? Ergo hoc quidem apparet, nos ad agendum esse natos. Illud non continuo, ut aeque incontentae.

+ + + +

Ea possunt paria non esse. Ita multa dicunt, quae vix intellegam. Nunc omni virtuti vitium contrario nomine opponitur. Quantum Aristoxeni ingenium consumptum videmus in musicis? Ergo instituto veterum, quo etiam Stoici utuntur, hinc capiamus exordium. Cur tantas regiones barbarorum pedibus obiit, tot maria transmisit? Idemne, quod iucunde? Egone quaeris, inquit, quid sentiam?

+ + + +

Quantum Aristoxeni ingenium consumptum videmus in musicis? Quid, quod res alia tota est? Sed ne, dum huic obsequor, vobis molestus sim. Transfer idem ad modestiam vel temperantiam, quae est moderatio cupiditatum rationi oboediens. Nescio quo modo praetervolavit oratio. Isto modo ne improbos quidem, si essent boni viri. Vide, ne etiam menses! nisi forte eum dicis, qui, simul atque arripuit, interficit. Id enim natura desiderat. Nulla profecto est, quin suam vim retineat a primo ad extremum. Quae quidem sapientes sequuntur duce natura tamquam videntes; Idemque diviserunt naturam hominis in animum et corpus. Quod autem ratione actum est, id officium appellamus.

+ + + +

Dicimus aliquem hilare vivere; Quia dolori non voluptas contraria est, sed doloris privatio. Semper enim ex eo, quod maximas partes continet latissimeque funditur, tota res appellatur. Graece donan, Latine voluptatem vocant. Hoc enim constituto in philosophia constituta sunt omnia. Tu autem inter haec tantam multitudinem hominum interiectam non vides nec laetantium nec dolentium? Atqui iste locus est, Piso, tibi etiam atque etiam confirmandus, inquam;

+ + + +

Cur, nisi quod turpis oratio est? Quare ad ea primum, si videtur; Quod autem principium officii quaerunt, melius quam Pyrrho; Dat enim intervalla et relaxat. Solum praeterea formosum, solum liberum, solum civem, stultost; Quod si ita se habeat, non possit beatam praestare vitam sapientia.

+ + + +
+

+ Id autem eius modi est, ut additum ad virtutem auctoritatem videatur habiturum et expleturum cumulate vitam beatam, de quo omnis haec quaestio est. +

+
+ + + +

Suam denique cuique naturam esse ad vivendum ducem. Igitur neque stultorum quisquam beatus neque sapientium non beatus. Et quidem, inquit, vehementer errat; Sed finge non solum callidum eum, qui aliquid improbe faciat, verum etiam praepotentem, ut M. Nunc de hominis summo bono quaeritur; Atqui reperies, inquit, in hoc quidem pertinacem; Cur tantas regiones barbarorum pedibus obiit, tot maria transmisit? Tum Piso: Quoniam igitur aliquid omnes, quid Lucius noster? Ut necesse sit omnium rerum, quae natura vigeant, similem esse finem, non eundem. Non risu potius quam oratione eiciendum? Bestiarum vero nullum iudicium puto.

+ + + +
+

+ Nec vero sum nescius esse utilitatem in historia, non modo voluptatem. +

+
+ + + +

Nam quibus rebus efficiuntur voluptates, eae non sunt in potestate sapientis.

+ + + +

Cur haec eadem Democritus? Mihi, inquam, qui te id ipsum rogavi? Semper enim ex eo, quod maximas partes continet latissimeque funditur, tota res appellatur. Ego vero isti, inquam, permitto. Quis istud possit, inquit, negare? Quam illa ardentis amores excitaret sui! Cur tandem?

+ + + +
Itaque vides, quo modo loquantur, nova verba fingunt, deserunt usitata.
+ + + +

Quo studio cum satiari non possint, omnium ceterarum rerum obliti níhil abiectum, nihil humile cogitant; Sin kakan malitiam dixisses, ad aliud nos unum certum vitium consuetudo Latina traduceret. Nam et a te perfici istam disputationem volo, nec tua mihi oratio longa videri potest. Sapientem locupletat ipsa natura, cuius divitias Epicurus parabiles esse docuit. Tum mihi Piso: Quid ergo? Quarum ambarum rerum cum medicinam pollicetur, luxuriae licentiam pollicetur. Vide, quantum, inquam, fallare, Torquate. Est, ut dicis, inquam. Maximus dolor, inquit, brevis est.

+ + + +
    +
  • Non modo carum sibi quemque, verum etiam vehementer carum esse?
  • + + + +
  • Et quoniam haec deducuntur de corpore quid est cur non recte pulchritudo etiam ipsa propter se expetenda ducatur?
  • + + + +
  • Cum autem venissemus in Academiae non sine causa nobilitata spatia, solitudo erat ea, quam volueramus.
  • +
+ + + +
+

+ Itaque omnis honos, omnis admiratio, omne studium ad virtutem et ad eas actiones, quae virtuti sunt consentaneae, refertur, eaque omnia, quae aut ita in animis sunt aut ita geruntur, uno nomine honesta dicuntur. +

+
+ + + +

Polemoni et iam ante Aristoteli ea prima visa sunt, quae paulo ante dixi. Tum Quintus: Est plane, Piso, ut dicis, inquit. Igitur ne dolorem quidem. Ut in geometria, prima si dederis, danda sunt omnia. Luxuriam non reprehendit, modo sit vacua infinita cupiditate et timore. Nec enim, dum metuit, iustus est, et certe, si metuere destiterit, non erit; An vero displicuit ea, quae tributa est animi virtutibus tanta praestantia?

+ + + +

Quis est tam dissimile homini. Hoc loco discipulos quaerere videtur, ut, qui asoti esse velint, philosophi ante fiant. In quibus doctissimi illi veteres inesse quiddam caeleste et divinum putaverunt. Quid nunc honeste dicit? Quam tu ponis in verbis, ego positam in re putabam.

+ + + +

Oratio me istius philosophi non offendit; Hic ambiguo ludimur. Inde sermone vario sex illa a Dipylo stadia confecimus. Nam, ut sint illa vendibiliora, haec uberiora certe sunt. Nummus in Croesi divitiis obscuratur, pars est tamen divitiarum. Neutrum vero, inquit ille. Sed ad haec, nisi molestum est, habeo quae velim. Parvi enim primo ortu sic iacent, tamquam omnino sine animo sint.

+ + + +

Deinde disputat, quod cuiusque generis animantium statui deceat extremum. Memini me adesse P. Qui-vere falsone, quaerere mittimus-dicitur oculis se privasse; Isto modo ne improbos quidem, si essent boni viri. His singulis copiose responderi solet, sed quae perspicua sunt longa esse non debent. Scrupulum, inquam, abeunti;

+ + + +

Qui enim voluptatem ipsam contemnunt, iis licet dicere se acupenserem maenae non anteponere.

+ + + +
+ + + +

Nec enim, dum metuit, iustus est, et certe, si metuere destiterit, non erit; Partim cursu et peragratione laetantur, congregatione aliae coetum quodam modo civitatis imitantur; Ergo et avarus erit, sed finite, et adulter, verum habebit modum, et luxuriosus eodem modo. Quid enim? Eam tum adesse, cum dolor omnis absit;

+ + + +

Nihil enim iam habes, quod ad corpus referas; Id est enim, de quo quaerimus. Quid autem habent admirationis, cum prope accesseris? Si mala non sunt, iacet omnis ratio Peripateticorum. Semovenda est igitur voluptas, non solum ut recta sequamini, sed etiam ut loqui deceat frugaliter. Sextilio Rufo, cum is rem ad amicos ita deferret, se esse heredem Q. An eum discere ea mavis, quae cum plane perdidiceriti nihil sciat? Nihilo magis. Sed emolumenta communia esse dicuntur, recte autem facta et peccata non habentur communia.

+ + + +

Quo modo autem optimum, si bonum praeterea nullum est?

+ + + +

Duae sunt enim res quoque, ne tu verba solum putes. Haec dicuntur inconstantissime. At ille pellit, qui permulcet sensum voluptate. Quod quidem iam fit etiam in Academia. Poterat autem inpune; Eaedem enim utilitates poterunt eas labefactare atque pervertere.

+ + + +
+

+ Lege laudationes, Torquate, non eorum, qui sunt ab Homero laudati, non Cyri, non Agesilai, non Aristidi aut Themistocli, non Philippi aut Alexandri, lege nostrorum hominum, lege vestrae familiae; +

+
+ + + +

Neque enim disputari sine reprehensione nec cum iracundia aut pertinacia recte disputari potest. Hunc vos beatum; Callipho ad virtutem nihil adiunxit nisi voluptatem, Diodorus vacuitatem doloris. Ergo hoc quidem apparet, nos ad agendum esse natos. Hoc est non modo cor non habere, sed ne palatum quidem. Ipse Epicurus fortasse redderet, ut Sextus Peducaeus, Sex. Qui-vere falsone, quaerere mittimus-dicitur oculis se privasse; Cum autem negant ea quicquam ad beatam vitam pertinere, rursus naturam relinquunt. Bonum negas esse divitias, praeposìtum esse dicis?

+ + + +
+

+ An potest, inquit ille, quicquam esse suavius quam nihil dolere? +

+
+ + + +

Tu quidem reddes; Efficiens dici potest. Et quidem, inquit, vehementer errat; Hoc sic expositum dissimile est superiori. Hic ego: Pomponius quidem, inquam, noster iocari videtur, et fortasse suo iure. A primo, ut opinor, animantium ortu petitur origo summi boni.

+ + + +
    +
  • At ille non pertimuit saneque fidenter: Istis quidem ipsis verbis, inquit;
  • + + + +
  • Nulla profecto est, quin suam vim retineat a primo ad extremum.
  • + + + +
  • Pisone in eo gymnasio, quod Ptolomaeum vocatur, unaque nobiscum Q.
  • + + + +
  • Apud ceteros autem philosophos, qui quaesivit aliquid, tacet;
  • +
+ + + +
    +
  • Sextilio Rufo, cum is rem ad amicos ita deferret, se esse heredem Q.
  • + + + +
  • Ergo id est convenienter naturae vivere, a natura discedere.
  • + + + +
  • Dici enim nihil potest verius.
  • +
+ + + +

Putabam equidem satis, inquit, me dixisse. Et quidem Arcesilas tuus, etsi fuit in disserendo pertinacior, tamen noster fuit; Audax negotium, dicerem impudens, nisi hoc institutum postea translatum ad philosophos nostros esset. Nam quibus rebus efficiuntur voluptates, eae non sunt in potestate sapientis. Equidem, sed audistine modo de Carneade? Nam ante Aristippus, et ille melius. Sed tamen intellego quid velit.

+ + + +
    +
  • Ex ea difficultate illae fallaciloquae, ut ait Accius, malitiae natae sunt.
  • + + + +
  • Sit hoc ultimum bonorum, quod nunc a me defenditur;
  • +
+ + + +
    +
  • Quae cum magnifice primo dici viderentur, considerata minus probabantur.
  • + + + +
  • Satisne igitur videor vim verborum tenere, an sum etiam nunc vel Graece loqui vel Latine docendus?
  • +
+ + + +

Quid affers, cur Thorius, cur Caius Postumius, cur omnium horum magister, Orata, non iucundissime vixerit? Videmusne ut pueri ne verberibus quidem a contemplandis rebus perquirendisque deterreantur? In his igitur partibus duabus nihil erat, quod Zeno commutare gestiret. Equidem soleo etiam quod uno Graeci, si aliter non possum, idem pluribus verbis exponere.

+ + + +

Nos cum te, M. Ab his oratores, ab his imperatores ac rerum publicarum principes extiterunt. Esse enim, nisi eris, non potes. At hoc in eo M. Ab his oratores, ab his imperatores ac rerum publicarum principes extiterunt. An ea, quae per vinitorem antea consequebatur, per se ipsa curabit? Virtutibus igitur rectissime mihi videris et ad consuetudinem nostrae orationis vitia posuisse contraria. Illa sunt similia: hebes acies est cuipiam oculorum, corpore alius senescit;

+ + + +
    +
  • Itaque nostrum est-quod nostrum dico, artis est-ad ea principia, quae accepimus.
  • + + + +
  • Quamquam haec quidem praeposita recte et reiecta dicere licebit.
  • + + + +
  • Aliter homines, aliter philosophos loqui putas oportere?
  • + + + +
  • Habent enim et bene longam et satis litigiosam disputationem.
  • + + + +
  • Sed non alienum est, quo facilius vis verbi intellegatur, rationem huius verbi faciendi Zenonis exponere.
  • + + + +
  • Tum Quintus: Est plane, Piso, ut dicis, inquit.
  • +
+ + + +
    +
  • Bonum integritas corporis: misera debilitas.
  • + + + +
  • Tu quidem reddes;
  • +
+ + + +
Neque enim disputari sine reprehensione nec cum iracundia aut pertinacia recte disputari potest.
+ + + +

Laboro autem non sine causa; Quid ei reliquisti, nisi te, quoquo modo loqueretur, intellegere, quid diceret? Aperiendum est igitur, quid sit voluptas; Quid enim possumus hoc agere divinius? Theophrasti igitur, inquit, tibi liber ille placet de beata vita? Nec tamen ullo modo summum pecudis bonum et hominis idem mihi videri potest. Quid loquor de nobis, qui ad laudem et ad decus nati, suscepti, instituti sumus? Pauca mutat vel plura sane; Intellegi quidem, ut propter aliam quampiam rem, verbi gratia propter voluptatem, nos amemus;

+ + + +
    +
  • Huic ego, si negaret quicquam interesse ad beate vivendum quali uteretur victu, concederem, laudarem etiam;
  • + + + +
  • Quod praeceptum quia maius erat, quam ut ab homine videretur, idcirco assignatum est deo.
  • + + + +
  • Videmusne ut pueri ne verberibus quidem a contemplandis rebus perquirendisque deterreantur?
  • + + + +
  • Scio enim esse quosdam, qui quavis lingua philosophari possint;
  • + + + +
  • Qua ex cognitione facilior facta est investigatio rerum occultissimarum.
  • +
+ + + +

Mihi enim satis est, ipsis non satis.

+ + + +

Quare conare, quaeso. Qui autem de summo bono dissentit de tota philosophiae ratione dissentit. Si longus, levis. Nam et complectitur verbis, quod vult, et dicit plane, quod intellegam; Cupiditates non Epicuri divisione finiebat, sed sua satietate.

+ + + +

Si verbum sequimur, primum longius verbum praepositum quam bonum.

+ + + +

Idemne, quod iucunde? Sin kakan malitiam dixisses, ad aliud nos unum certum vitium consuetudo Latina traduceret. Ac tamen hic mallet non dolere. Praeclare hoc quidem. Neque enim disputari sine reprehensione nec cum iracundia aut pertinacia recte disputari potest. Quicquid enim a sapientia proficiscitur, id continuo debet expletum esse omnibus suis partibus; Sed ad rem redeamus; Quasi vero, inquit, perpetua oratio rhetorum solum, non etiam philosophorum sit.

+ + + +

Restincta enim sitis stabilitatem voluptatis habet, inquit, illa autem voluptas ipsius restinctionis in motu est. Illis videtur, qui illud non dubitant bonum dicere -; Tum Torquatus: Prorsus, inquit, assentior; Quis enim confidit semper sibi illud stabile et firmum permansurum, quod fragile et caducum sit? Vitae autem degendae ratio maxime quidem illis placuit quieta. Quantum Aristoxeni ingenium consumptum videmus in musicis?

+ + + +

Rationis enim perfectio est virtus; Sed tu istuc dixti bene Latine, parum plane. At coluit ipse amicitias. Illa videamus, quae a te de amicitia dicta sunt. Tenent mordicus. Quamquam tu hanc copiosiorem etiam soles dicere.

+ + + +
Respondent extrema primis, media utrisque, omnia omnibus.
+ + + +

Istam voluptatem perpetuam quis potest praestare sapienti? Nam illud vehementer repugnat, eundem beatum esse et multis malis oppressum. Bonum incolumis acies: misera caecitas. Quae duo sunt, unum facit. Conferam avum tuum Drusum cum C.

+ + + +

Primum divisit ineleganter; Sullae consulatum? Nam memini etiam quae nolo, oblivisci non possum quae volo. Ne amores quidem sanctos a sapiente alienos esse arbitrantur. Ex quo, id quod omnes expetunt, beate vivendi ratio inveniri et comparari potest. In his igitur partibus duabus nihil erat, quod Zeno commutare gestiret. Sed residamus, inquit, si placet. Vitae autem degendae ratio maxime quidem illis placuit quieta.

+ + + +
Bona autem corporis huic sunt, quod posterius posui, similiora.
+ + + +

Ergo instituto veterum, quo etiam Stoici utuntur, hinc capiamus exordium. Et harum quidem rerum facilis est et expedita distinctio. Quid igitur dubitamus in tota eius natura quaerere quid sit effectum? Tu autem negas fortem esse quemquam posse, qui dolorem malum putet.

+ + + +

Atqui eorum nihil est eius generis, ut sit in fine atque extrerno bonorum. In quibus doctissimi illi veteres inesse quiddam caeleste et divinum putaverunt. Deinde prima illa, quae in congressu solemus: Quid tu, inquit, huc? Quod cum ille dixisset et satis disputatum videretur, in oppidum ad Pomponium perreximus omnes. Ut placet, inquit, etsi enim illud erat aptius, aequum cuique concedere. Nec vero sum nescius esse utilitatem in historia, non modo voluptatem. Tum ille timide vel potius verecunde: Facio, inquit. Idem iste, inquam, de voluptate quid sentit?

+ + + +
Sed erat aequius Triarium aliquid de dissensione nostra iudicare.
+ + + +

Negat enim summo bono afferre incrementum diem. Habent enim et bene longam et satis litigiosam disputationem. Aut haec tibi, Torquate, sunt vituperanda aut patrocinium voluptatis repudiandum. Ut proverbia non nulla veriora sint quam vestra dogmata. Fortitudinis quaedam praecepta sunt ac paene leges, quae effeminari virum vetant in dolore. Eam tum adesse, cum dolor omnis absit; Estne, quaeso, inquam, sitienti in bibendo voluptas? Negat esse eam, inquit, propter se expetendam.

+ + + +

Etenim semper illud extra est, quod arte comprehenditur. Quid ei reliquisti, nisi te, quoquo modo loqueretur, intellegere, quid diceret? Experiamur igitur, inquit, etsi habet haec Stoicorum ratio difficilius quiddam et obscurius. Virtutis, magnitudinis animi, patientiae, fortitudinis fomentis dolor mitigari solet.

+ + + +
    +
  • Sed quid ages tandem, si utilitas ab amicitia, ut fit saepe, defecerit?
  • + + + +
  • Sed nunc, quod agimus;
  • + + + +
  • Beatus autem esse in maximarum rerum timore nemo potest.
  • + + + +
  • An ea, quae per vinitorem antea consequebatur, per se ipsa curabit?
  • + + + +
  • Sedulo, inquam, faciam.
  • +
+ + + +

Morbo gravissimo affectus, exul, orbus, egens, torqueatur eculeo: quem hunc appellas, Zeno? Ergo instituto veterum, quo etiam Stoici utuntur, hinc capiamus exordium. Restinguet citius, si ardentem acceperit. Hanc ergo intuens debet institutum illud quasi signum absolvere. Primum Theophrasti, Strato, physicum se voluit; Est enim effectrix multarum et magnarum voluptatum. Summus dolor plures dies manere non potest? Sed ille, ut dixi, vitiose.

+ + + +

Quid ergo attinet gloriose loqui, nisi constanter loquare? Ut id aliis narrare gestiant? Inde sermone vario sex illa a Dipylo stadia confecimus. Graecis hoc modicum est: Leonidas, Epaminondas, tres aliqui aut quattuor; Non enim iam stirpis bonum quaeret, sed animalis. Rationis enim perfectio est virtus; Ita graviter et severe voluptatem secrevit a bono. Ostendit pedes et pectus. At modo dixeras nihil in istis rebus esse, quod interesset. Praeclarae mortes sunt imperatoriae; Eam stabilem appellas.

+ + + +
    +
  • Ab his oratores, ab his imperatores ac rerum publicarum principes extiterunt.
  • + + + +
  • Quae sunt igitur communia vobis cum antiquis, iis sic utamur quasi concessis;
  • + + + +
  • Tamen aberramus a proposito, et, ne longius, prorsus, inquam, Piso, si ista mala sunt, placet.
  • + + + +
  • Sit hoc ultimum bonorum, quod nunc a me defenditur;
  • + + + +
  • Sed quid attinet de rebus tam apertis plura requirere?
  • + + + +
  • Nam si propter voluptatem, quae est ista laus, quae possit e macello peti?
  • +
+ + + +

Verba tu fingas et ea dicas, quae non sentias? Praeclare hoc quidem. Quid ait Aristoteles reliquique Platonis alumni? Iam id ipsum absurdum, maximum malum neglegi. Sed tamen omne, quod de re bona dilucide dicitur, mihi praeclare dici videtur. Urgent tamen et nihil remittunt. Miserum hominem! Si dolor summum malum est, dici aliter non potest. Summum ením bonum exposuit vacuitatem doloris;

+ + + +

Nam, ut sint illa vendibiliora, haec uberiora certe sunt. Transfer idem ad modestiam vel temperantiam, quae est moderatio cupiditatum rationi oboediens. Ergo id est convenienter naturae vivere, a natura discedere.

+ + + +
Profectus in exilium Tubulus statim nec respondere ausus;
+ + + +

Sed tu istuc dixti bene Latine, parum plane. Quae similitudo in genere etiam humano apparet. Semovenda est igitur voluptas, non solum ut recta sequamini, sed etiam ut loqui deceat frugaliter. An est aliquid per se ipsum flagitiosum, etiamsi nulla comitetur infamia? Quae in controversiam veniunt, de iis, si placet, disseramus. Quid iudicant sensus? Quonam, inquit, modo? Cur post Tarentum ad Archytam?

+ + + +

Scaevolam M. Prodest, inquit, mihi eo esse animo. Est enim effectrix multarum et magnarum voluptatum. Aliter enim explicari, quod quaeritur, non potest. De quibus cupio scire quid sentias.

+ + + +

Laboro autem non sine causa; Quis non odit sordidos, vanos, leves, futtiles? Sedulo, inquam, faciam. Summum a vobis bonum voluptas dicitur. Qui igitur convenit ab alia voluptate dicere naturam proficisci, in alia summum bonum ponere? Illi enim inter se dissentiunt. Qui ita affectus, beatum esse numquam probabis; Idemque diviserunt naturam hominis in animum et corpus.

+ + + +

Quae cum essent dicta, discessimus. Laboro autem non sine causa; Duo enim genera quae erant, fecit tria. Hoc loco tenere se Triarius non potuit. Sed residamus, inquit, si placet. Falli igitur possumus. Torquatus, is qui consul cum Cn. Sit hoc ultimum bonorum, quod nunc a me defenditur; Ne discipulum abducam, times. Prodest, inquit, mihi eo esse animo. Tum Piso: Quoniam igitur aliquid omnes, quid Lucius noster?

+ + + +
Quid ei reliquisti, nisi te, quoquo modo loqueretur, intellegere, quid diceret?
+ + + +

Illud non continuo, ut aeque incontentae. Respondent extrema primis, media utrisque, omnia omnibus. Si mala non sunt, iacet omnis ratio Peripateticorum. Inde igitur, inquit, ordiendum est. Nonne igitur tibi videntur, inquit, mala? Quam ob rem tandem, inquit, non satisfacit? Est tamen ea secundum naturam multoque nos ad se expetendam magis hortatur quam superiora omnia. Itaque hic ipse iam pridem est reiectus;

+ + + +
    +
  • Minime vero, inquit ille, consentit.
  • + + + +
  • Sunt enim quasi prima elementa naturae, quibus ubertas orationis adhiberi vix potest, nec equidem eam cogito consectari.
  • + + + +
  • At coluit ipse amicitias.
  • + + + +
  • Ex quo illud efficitur, qui bene cenent omnis libenter cenare, qui libenter, non continuo bene.
  • + + + +
  • Diodorus, eius auditor, adiungit ad honestatem vacuitatem doloris.
  • +
+ + + +
+ + + +
Quamquam te quidem video minime esse deterritum.
+ + + +

At Zeno eum non beatum modo, sed etiam divitem dicere ausus est. Quid de Pythagora? Non enim quaero quid verum, sed quid cuique dicendum sit. Sint ista Graecorum; Non ego tecum iam ita iocabor, ut isdem his de rebus, cum L. Cupit enim dícere nihil posse ad beatam vitam deesse sapienti. Quodsi ipsam honestatem undique pertectam atque absolutam.

+ + + +
+

+ Quod cum dixissent, ille contra. +

+
+ + + +

Eadem nunc mea adversum te oratio est. Oratio me istius philosophi non offendit; Totum genus hoc Zeno et qui ab eo sunt aut non potuerunt aut noluerunt, certe reliquerunt. Quacumque enim ingredimur, in aliqua historia vestigium ponimus. Expressa vero in iis aetatibus, quae iam confirmatae sunt. Ab his oratores, ab his imperatores ac rerum publicarum principes extiterunt. Tum Piso: Quoniam igitur aliquid omnes, quid Lucius noster? Quid igitur dubitamus in tota eius natura quaerere quid sit effectum? Que Manilium, ab iisque M. At multis malis affectus.

+ + + +
    +
  • Traditur, inquit, ab Epicuro ratio neglegendi doloris.
  • + + + +
  • Sed tamen omne, quod de re bona dilucide dicitur, mihi praeclare dici videtur.
  • +
+ + + +

Primum in nostrane potestate est, quid meminerimus?

+ + + +

Et ille ridens: Video, inquit, quid agas; Si longus, levis dictata sunt. Hoc loco tenere se Triarius non potuit. Quid Zeno? Nam et a te perfici istam disputationem volo, nec tua mihi oratio longa videri potest. Estne, quaeso, inquam, sitienti in bibendo voluptas?

+ + + +

Videamus animi partes, quarum est conspectus illustrior;

+ + + +

Summum ením bonum exposuit vacuitatem doloris; At cum de plurimis eadem dicit, tum certe de maximis. Maximus dolor, inquit, brevis est. Qua tu etiam inprudens utebare non numquam. Universa enim illorum ratione cum tota vestra confligendum puto. Multa sunt dicta ab antiquis de contemnendis ac despiciendis rebus humanis; In omni enim arte vel studio vel quavis scientia vel in ipsa virtute optimum quidque rarissimum est. Maximas vero virtutes iacere omnis necesse est voluptate dominante.

+ + + +

Tantum dico, magis fuisse vestrum agere Epicuri diem natalem, quam illius testamento cavere ut ageretur. Si stante, hoc natura videlicet vult, salvam esse se, quod concedimus; Ut in voluptate sit, qui epuletur, in dolore, qui torqueatur. Eaedem res maneant alio modo. Itaque si aut requietem natura non quaereret aut eam posset alia quadam ratione consequi.

+ + + +

In eo enim positum est id, quod dicimus esse expetendum.

+ + + +

Multoque hoc melius nos veriusque quam Stoici. Negat esse eam, inquit, propter se expetendam. Sed quanta sit alias, nunc tantum possitne esse tanta. Idemne potest esse dies saepius, qui semel fuit? At Zeno eum non beatum modo, sed etiam divitem dicere ausus est. Nemo nostrum istius generis asotos iucunde putat vivere. Deinde disputat, quod cuiusque generis animantium statui deceat extremum.

+ + + +
Efficiens dici potest.
+ + + +

Num igitur eum postea censes anxio animo aut sollicito fuisse? Nec vero pietas adversus deos nec quanta iis gratia debeatur sine explicatione naturae intellegi potest. Quod ea non occurrentia fingunt, vincunt Aristonem; Tecum optime, deinde etiam cum mediocri amico. Sed tamen enitar et, si minus multa mihi occurrent, non fugiam ista popularia. Isto modo ne improbos quidem, si essent boni viri. Non semper, inquam; Quid interest, nisi quod ego res notas notis verbis appello, illi nomina nova quaerunt, quibus idem dicant? Tecum optime, deinde etiam cum mediocri amico. Ergo in gubernando nihil, in officio plurimum interest, quo in genere peccetur.

+ + + +
    +
  • Immo istud quidem, inquam, quo loco quidque, nisi iniquum postulo, arbitratu meo.
  • + + + +
  • Cur ipse Pythagoras et Aegyptum lustravit et Persarum magos adiit?
  • + + + +
  • Ergo illi intellegunt quid Epicurus dicat, ego non intellego?
  • +
+ + + +

Sed in rebus apertissimis nimium longi sumus. Collige omnia, quae soletis: Praesidium amicorum. At ego quem huic anteponam non audeo dicere; Sed hoc sane concedamus. At iam decimum annum in spelunca iacet. Fatebuntur Stoici haec omnia dicta esse praeclare, neque eam causam Zenoni desciscendi fuisse. Vide, quantum, inquam, fallare, Torquate.

+ + + +
Quae duo sunt, unum facit.
+ + + +

Quid sequatur, quid repugnet, vident. Tu autem, si tibi illa probabantur, cur non propriis verbis ea tenebas? Piso, familiaris noster, et alia multa et hoc loco Stoicos irridebat: Quid enim? Cur deinde Metrodori liberos commendas?

+ + + +
+

+ Quod et posse fieri intellegimus et saepe etiam videmus, et perspicuum est nihil ad iucunde vivendum reperiri posse, quod coniunctione tali sit aptius. +

+
+ + + +

Tu autem, si tibi illa probabantur, cur non propriis verbis ea tenebas? Portenta haec esse dicit, neque ea ratione ullo modo posse vivi; Sed hoc sane concedamus. Si alia sentit, inquam, alia loquitur, numquam intellegam quid sentiat; Vestri haec verecundius, illi fortasse constantius. Nam aliquando posse recte fieri dicunt nulla expectata nec quaesita voluptate. A quibus propter discendi cupiditatem videmus ultimas terras esse peragratas. Quid ergo hoc loco intellegit honestum? Propter nos enim illam, non propter eam nosmet ipsos diligimus.

+ + + +

Faceres tu quidem, Torquate, haec omnia;

+ + + +

Neque enim civitas in seditione beata esse potest nec in discordia dominorum domus; Si autem id non concedatur, non continuo vita beata tollitur. Nam Pyrrho, Aristo, Erillus iam diu abiecti. Aliter enim nosmet ipsos nosse non possumus. Idem iste, inquam, de voluptate quid sentit? Eam tum adesse, cum dolor omnis absit; Iam in altera philosophiae parte. At multis malis affectus. Quae cum ita sint, effectum est nihil esse malum, quod turpe non sit.

+ + + +

Haec dicuntur inconstantissime. Nos paucis ad haec additis finem faciamus aliquando; Ut in geometria, prima si dederis, danda sunt omnia. At multis se probavit. Nam adhuc, meo fortasse vitio, quid ego quaeram non perspicis. Itaque his sapiens semper vacabit. Satis est ad hoc responsum. Ita enim vivunt quidam, ut eorum vita refellatur oratio.

+ + + +

Quid censes in Latino fore? Superiores tres erant, quae esse possent, quarum est una sola defensa, eaque vehementer. Ego vero volo in virtute vim esse quam maximam; Neque solum ea communia, verum etiam paria esse dixerunt.

+ + + +
    +
  • Et ais, si una littera commota sit, fore tota ut labet disciplina.
  • + + + +
  • Mihi enim satis est, ipsis non satis.
  • + + + +
  • Sic vester sapiens magno aliquo emolumento commotus cicuta, si opus erit, dimicabit.
  • + + + +
  • Satisne igitur videor vim verborum tenere, an sum etiam nunc vel Graece loqui vel Latine docendus?
  • + + + +
  • Tum Torquatus: Prorsus, inquit, assentior;
  • + + + +
  • Sed quoniam et advesperascit et mihi ad villam revertendum est, nunc quidem hactenus;
  • +
+ + + +

Nam ista vestra: Si gravis, brevis; Hic Speusippus, hic Xenocrates, hic eius auditor Polemo, cuius illa ipsa sessio fuit, quam videmus. Laboro autem non sine causa; Recte, inquit, intellegis. Quare obscurentur etiam haec, quae secundum naturam esse dicimus, in vita beata;

+ + + +
+

+ Quibus autem in rebus tanta obscuratio non fit, fieri tamen potest, ut id ipsum, quod interest, non sit magnum. +

+
+ + + +

Quamquam tu hanc copiosiorem etiam soles dicere. Et quidem iure fortasse, sed tamen non gravissimum est testimonium multitudinis. Atque hoc loco similitudines eas, quibus illi uti solent, dissimillimas proferebas. Eadem fortitudinis ratio reperietur. Age, inquies, ista parva sunt. Rationis enim perfectio est virtus; Etsi ea quidem, quae adhuc dixisti, quamvis ad aetatem recte isto modo dicerentur.

+ + + +

Ergo id est convenienter naturae vivere, a natura discedere. Si de re disceptari oportet, nulla mihi tecum, Cato, potest esse dissensio. Quod si ita sit, cur opera philosophiae sit danda nescio. Conferam avum tuum Drusum cum C. Unum nescio, quo modo possit, si luxuriosus sit, finitas cupiditates habere. Facile est hoc cernere in primis puerorum aetatulis. Mihi quidem Antiochum, quem audis, satis belle videris attendere. Sed haec quidem liberius ab eo dicuntur et saepius. Sed non alienum est, quo facilius vis verbi intellegatur, rationem huius verbi faciendi Zenonis exponere. Duo enim genera quae erant, fecit tria. Sit sane ista voluptas.

+ + + +

Quo tandem modo? Quos quidem tibi studiose et diligenter tractandos magnopere censeo. Eam tum adesse, cum dolor omnis absit; Sed tamen est aliquid, quod nobis non liceat, liceat illis. Quid est, quod ab ea absolvi et perfici debeat? Videamus animi partes, quarum est conspectus illustrior; Cui Tubuli nomen odio non est? Non est igitur summum malum dolor.

+ + + +

Sed quid ages tandem, si utilitas ab amicitia, ut fit saepe, defecerit?

+ + + +

Quod cum accidisset ut alter alterum necopinato videremus, surrexit statim. Ut non sine causa ex iis memoriae ducta sit disciplina. Satis est tibi in te, satis in legibus, satis in mediocribus amicitiis praesidii. Quo plebiscito decreta a senatu est consuli quaestio Cn. In his igitur partibus duabus nihil erat, quod Zeno commutare gestiret. Negat esse eam, inquit, propter se expetendam. Haec para/doca illi, nos admirabilia dicamus. Itaque nostrum est-quod nostrum dico, artis est-ad ea principia, quae accepimus.

+ + + +

Aeque enim contingit omnibus fidibus, ut incontentae sint.

+ + + +

Si verbum sequimur, primum longius verbum praepositum quam bonum. Dat enim intervalla et relaxat. Sed tamen enitar et, si minus multa mihi occurrent, non fugiam ista popularia. Scrupulum, inquam, abeunti; Atque haec ita iustitiae propria sunt, ut sint virtutum reliquarum communia. Et quidem iure fortasse, sed tamen non gravissimum est testimonium multitudinis. Quid, de quo nulla dissensio est? Paulum, cum regem Persem captum adduceret, eodem flumine invectio? An nisi populari fama? Transfer idem ad modestiam vel temperantiam, quae est moderatio cupiditatum rationi oboediens.

+ + + +
    +
  • Nec enim, dum metuit, iustus est, et certe, si metuere destiterit, non erit;
  • + + + +
  • Ab hoc autem quaedam non melius quam veteres, quaedam omnino relicta.
  • + + + +
  • Si enim ita est, vide ne facinus facias, cum mori suadeas.
  • + + + +
  • Nec enim ignoras his istud honestum non summum modo, sed etiam, ut tu vis, solum bonum videri.
  • + + + +
  • Vos autem cum perspicuis dubia debeatis illustrare, dubiis perspicua conamini tollere.
  • + + + +
  • Idcirco enim non desideraret, quia, quod dolore caret, id in voluptate est.
  • +
+ + + +

Nam cui proposito sit conservatio sui, necesse est huic partes quoque sui caras suo genere laudabiles. Mihi quidem Antiochum, quem audis, satis belle videris attendere.

+ + + +

Tamen a proposito, inquam, aberramus. An hoc usque quaque, aliter in vita? Roges enim Aristonem, bonane ei videantur haec: vacuitas doloris, divitiae, valitudo; Nemo igitur esse beatus potest. Efficiens dici potest. Ut scias me intellegere, primum idem esse dico voluptatem, quod ille don. Nihil illinc huc pervenit.

+ + + +
    +
  • Pisone in eo gymnasio, quod Ptolomaeum vocatur, unaque nobiscum Q.
  • + + + +
  • Non igitur bene.
  • +
+ + + +

Videsne quam sit magna dissensio?

+ + + +

Quare conare, quaeso. Ad corpus diceres pertinere-, sed ea, quae dixi, ad corpusne refers? Qualem igitur hominem natura inchoavit? Ait enim se, si uratur, Quam hoc suave! dicturum.

+ + + +
+

+ Cuius etiam illi hortuli propinqui non memoriam solum mihi afferunt, sed ipsum videntur in conspectu meo ponere. +

+
+ + + +

Perge porro;

+ + + +

Cur tantas regiones barbarorum pedibus obiit, tot maria transmisit? Tum Quintus: Est plane, Piso, ut dicis, inquit. Sextilio Rufo, cum is rem ad amicos ita deferret, se esse heredem Q. Etiam beatissimum? Certe nihil nisi quod possit ipsum propter se iure laudari. Bestiarum vero nullum iudicium puto. Dat enim intervalla et relaxat.

+ + + +
+

+ Restat locus huic disputationi vel maxime necessarius de amicitia, quam, si voluptas summum sit bonum, affirmatis nullam omnino fore. +

+
+ + + +

Quid dubitas igitur mutare principia naturae?

+ + + +

Torquatus, is qui consul cum Cn. Miserum hominem! Si dolor summum malum est, dici aliter non potest. Age sane, inquam. Ergo hoc quidem apparet, nos ad agendum esse natos. Qui potest igitur habitare in beata vita summi mali metus? Dolor ergo, id est summum malum, metuetur semper, etiamsi non aderit; Certe non potest. Graecis hoc modicum est: Leonidas, Epaminondas, tres aliqui aut quattuor;

+ + + +

Illud non continuo, ut aeque incontentae.

+ + + +

Non est enim vitium in oratione solum, sed etiam in moribus. An me, inquam, nisi te audire vellem, censes haec dicturum fuisse? Simus igitur contenti his. Multoque hoc melius nos veriusque quam Stoici. Proclivi currit oratio.

+ + + +
Quid enim tanto opus est instrumento in optimis artibus comparandis?
+ + + +

Ne in odium veniam, si amicum destitero tueri. At quanta conantur! Mundum hunc omnem oppidum esse nostrum! Incendi igitur eos, qui audiunt, vides. Tu vero, inquam, ducas licet, si sequetur; Et nunc quidem quod eam tuetur, ut de vite potissimum loquar, est id extrinsecus; Illa argumenta propria videamus, cur omnia sint paria peccata. Illa videamus, quae a te de amicitia dicta sunt. Sed nimis multa. Non autem hoc: igitur ne illud quidem. Sed residamus, inquit, si placet. Nam si beatus umquam fuisset, beatam vitam usque ad illum a Cyro extructum rogum pertulisset. Illud dico, ea, quae dicat, praeclare inter se cohaerere.

+ + + +
+

+ Quae possunt eadem contra Carneadeum illud summum bonum dici, quod is non tam, ut probaret, protulit, quam ut Stoicis, quibuscum bellum gerebat, opponeret. +

+
+ + + +

Nihil sane. At multis se probavit. Sed est forma eius disciplinae, sicut fere ceterarum, triplex: una pars est naturae, disserendi altera, vivendi tertia. Potius inflammat, ut coercendi magis quam dedocendi esse videantur. Nihil opus est exemplis hoc facere longius. Minime id quidem, inquam, alienum, multumque ad ea, quae quaerimus, explicatio tua ista profecerit. Quibusnam praeteritis? Quae quidem sapientes sequuntur duce natura tamquam videntes;

+ + + +
At quicum ioca seria, ut dicitur, quicum arcana, quicum occulta omnia?
+ + + +

Hoc loco discipulos quaerere videtur, ut, qui asoti esse velint, philosophi ante fiant. Sint ista Graecorum; Si quae forte-possumus. Servari enim iustitia nisi a forti viro, nisi a sapiente non potest. Atqui reperies, inquit, in hoc quidem pertinacem; Piso, familiaris noster, et alia multa et hoc loco Stoicos irridebat: Quid enim?

+ + + +
    +
  • Qua ex cognitione facilior facta est investigatio rerum occultissimarum.
  • + + + +
  • Sed quae tandem ista ratio est?
  • + + + +
  • Tum Piso: Quoniam igitur aliquid omnes, quid Lucius noster?
  • + + + +
  • Nihil minus, contraque illa hereditate dives ob eamque rem laetus.
  • +
+ + + +
+

+ Quis enim tam inimicus paene nomini Romano est, qui Ennii Medeam aut Antiopam Pacuvii spernat aut reiciat, quod se isdem Euripidis fabulis delectari dicat, Latinas litteras oderit? +

+
+ + + +

Deinde qui fit, ut ego nesciam, sciant omnes, quicumque Epicurei esse voluerunt? Philosophi autem in suis lectulis plerumque moriuntur. Ergo adhuc, quantum equidem intellego, causa non videtur fuisse mutandi nominis.

+ + + +

Non quam nostram quidem, inquit Pomponius iocans; Ergo ita: non posse honeste vivi, nisi honeste vivatur? Neque enim disputari sine reprehensione nec cum iracundia aut pertinacia recte disputari potest. Quae similitudo in genere etiam humano apparet.

+ + + +
+

+ Etsi dedit talem mentem, quae omnem virtutem accipere posset, ingenuitque sine doctrina notitias parvas rerum maximarum et quasi instituit docere et induxit in ea, quae inerant, tamquam elementa virtutis. +

+
+ + + +

Equidem, sed audistine modo de Carneade? Si quicquam extra virtutem habeatur in bonis. Graece donan, Latine voluptatem vocant. Quorum sine causa fieri nihil putandum est. Quis animo aequo videt eum, quem inpure ac flagitiose putet vivere? Unum est sine dolore esse, alterum cum voluptate. Intellegi quidem, ut propter aliam quampiam rem, verbi gratia propter voluptatem, nos amemus; Iam quae corporis sunt, ea nec auctoritatem cum animi partibus, comparandam et cognitionem habent faciliorem.

+ + + +

Gerendus est mos, modo recte sentiat.

+ + + +

Ergo instituto veterum, quo etiam Stoici utuntur, hinc capiamus exordium. Immo vero, inquit, ad beatissime vivendum parum est, ad beate vero satis. Si stante, hoc natura videlicet vult, salvam esse se, quod concedimus; Rationis enim perfectio est virtus; Ut pulsi recurrant? Inde sermone vario sex illa a Dipylo stadia confecimus. Quae quidem sapientes sequuntur duce natura tamquam videntes; Ne discipulum abducam, times.

+ + + +

Atqui iste locus est, Piso, tibi etiam atque etiam confirmandus, inquam; Tollenda est atque extrahenda radicitus. Bonum incolumis acies: misera caecitas. Nec vero alia sunt quaerenda contra Carneadeam illam sententiam.

+ + + +
    +
  • Illa sunt similia: hebes acies est cuipiam oculorum, corpore alius senescit;
  • + + + +
  • Eodem modo is enim tibi nemo dabit, quod, expetendum sit, id esse laudabile.
  • + + + +
  • Quarum ambarum rerum cum medicinam pollicetur, luxuriae licentiam pollicetur.
  • +
+ + + +

Quid turpius quam sapientis vitam ex insipientium sermone pendere? Quia dolori non voluptas contraria est, sed doloris privatio. Ne amores quidem sanctos a sapiente alienos esse arbitrantur.

+ + + +

Quis hoc dicit? Quis est enim, in quo sit cupiditas, quin recte cupidus dici possit? Et harum quidem rerum facilis est et expedita distinctio. Dic in quovis conventu te omnia facere, ne doleas. An nisi populari fama? Idem iste, inquam, de voluptate quid sentit?

+ + + +

Nos commodius agimus.

+ + + +

Hanc in motu voluptatem -sic enim has suaves et quasi dulces voluptates appellat-interdum ita extenuat, ut M. Itaque rursus eadem ratione, qua sum paulo ante usus, haerebitis. Nonne videmus quanta perturbatio rerum omnium consequatur, quanta confusio? Maximus dolor, inquit, brevis est. Deprehensus omnem poenam contemnet. Hoc Hieronymus summum bonum esse dixit.

+ + + +
+ + + +

Compensabatur, inquit, cum summis doloribus laetitia. Urgent tamen et nihil remittunt. Tu vero, inquam, ducas licet, si sequetur; Sedulo, inquam, faciam.

+ + + +

Quis enim confidit semper sibi illud stabile et firmum permansurum, quod fragile et caducum sit?

+ + + +

Audax negotium, dicerem impudens, nisi hoc institutum postea translatum ad philosophos nostros esset. Sin autem eos non probabat, quid attinuit cum iis, quibuscum re concinebat, verbis discrepare? Nam quibus rebus efficiuntur voluptates, eae non sunt in potestate sapientis. Sin dicit obscurari quaedam nec apparere, quia valde parva sint, nos quoque concedimus; Conferam tecum, quam cuique verso rem subicias;

+ + + +

Si quicquam extra virtutem habeatur in bonis. Qui bonum omne in virtute ponit, is potest dicere perfici beatam vitam perfectione virtutis; Vos autem cum perspicuis dubia debeatis illustrare, dubiis perspicua conamini tollere. His enim rebus detractis negat se reperire in asotorum vita quod reprehendat. Non quaero, quid dicat, sed quid convenienter possit rationi et sententiae suae dicere. Non enim iam stirpis bonum quaeret, sed animalis.

+ + + +

Igitur neque stultorum quisquam beatus neque sapientium non beatus. Minime vero, inquit ille, consentit. Tum Quintus: Est plane, Piso, ut dicis, inquit. Quid autem habent admirationis, cum prope accesseris? Res enim concurrent contrariae. Satisne vobis videor pro meo iure in vestris auribus commentatus? Videmus in quodam volucrium genere non nulla indicia pietatis, cognitionem, memoriam, in multis etiam desideria videmus. Quis istud possit, inquit, negare?

+ + + +

Quae quidem sapientes sequuntur duce natura tamquam videntes; Ita enim vivunt quidam, ut eorum vita refellatur oratio. De hominibus dici non necesse est. Eam tum adesse, cum dolor omnis absit; At enim sequor utilitatem. Quod autem principium officii quaerunt, melius quam Pyrrho;

+ + + +
    +
  • Quaesita enim virtus est, non quae relinqueret naturam, sed quae tueretur.
  • + + + +
  • Plane idem, inquit, et maxima quidem, qua fieri nulla maior potest.
  • + + + +
  • Quod si ita est, sequitur id ipsum, quod te velle video, omnes semper beatos esse sapientes.
  • + + + +
  • A villa enim, credo, et: Si ibi te esse scissem, ad te ipse venissem.
  • +
+ + + +

Sed potestne rerum maior esse dissensio?

+ + + +

Indicant pueri, in quibus ut in speculis natura cernitur. Rem unam praeclarissimam omnium maximeque laudandam, penitus viderent, quonam gaudio complerentur, cum tantopere eius adumbrata opinione laetentur? Ait enim se, si uratur, Quam hoc suave! dicturum. Atqui eorum nihil est eius generis, ut sit in fine atque extrerno bonorum.

+ + + +

At multis se probavit. Non est enim vitium in oratione solum, sed etiam in moribus. Invidiosum nomen est, infame, suspectum. Cetera illa adhibebat, quibus demptis negat se Epicurus intellegere quid sit bonum. Quam ob rem tandem, inquit, non satisfacit? An vero displicuit ea, quae tributa est animi virtutibus tanta praestantia? Atqui iste locus est, Piso, tibi etiam atque etiam confirmandus, inquam; Nunc vides, quid faciat. Frater et T. Tanti autem aderant vesicae et torminum morbi, ut nihil ad eorum magnitudinem posset accedere.

+ + + +

Et ille ridens: Video, inquit, quid agas;

+ + + +

Te enim iudicem aequum puto, modo quae dicat ille bene noris. Non est ista, inquam, Piso, magna dissensio. Ita cum ea volunt retinere, quae superiori sententiae conveniunt, in Aristonem incidunt; Ego quoque, inquit, didicerim libentius si quid attuleris, quam te reprehenderim. Que Manilium, ab iisque M. Sic, et quidem diligentius saepiusque ista loquemur inter nos agemusque communiter. Hoc est non modo cor non habere, sed ne palatum quidem. Nobis Heracleotes ille Dionysius flagitiose descivisse videtur a Stoicis propter oculorum dolorem.

+ + + +

Tu autem negas fortem esse quemquam posse, qui dolorem malum putet. Portenta haec esse dicit, neque ea ratione ullo modo posse vivi; Nec vero alia sunt quaerenda contra Carneadeam illam sententiam. Ut proverbia non nulla veriora sint quam vestra dogmata. Qui autem esse poteris, nisi te amor ipse ceperit? Ego vero volo in virtute vim esse quam maximam; Cur fortior sit, si illud, quod tute concedis, asperum et vix ferendum putabit? Si stante, hoc natura videlicet vult, salvam esse se, quod concedimus;

+ + + +
+ + + +
+

+ Quos ille, di inmortales, cum omnes artus ardere viderentur, cruciatus perferebat! nec tamen miser esse, quia summum id malum non erat, tantum modo laboriosus videbatur; +

+
+ + + +

Intrandum est igitur in rerum naturam et penitus quid ea postulet pervidendum; Vide, quaeso, rectumne sit. Est enim tanti philosophi tamque nobilis audacter sua decreta defendere. Tum Piso: Quoniam igitur aliquid omnes, quid Lucius noster? Quae cum dixisset paulumque institisset, Quid est? Quae sequuntur igitur?

+ + + +
    +
  • Eadem fortitudinis ratio reperietur.
  • + + + +
  • Quid, quod homines infima fortuna, nulla spe rerum gerendarum, opifices denique delectantur historia?
  • + + + +
  • Cum ageremus, inquit, vitae beatum et eundem supremum diem, scribebamus haec.
  • + + + +
  • Scientiam pollicentur, quam non erat mirum sapientiae cupido patria esse cariorem.
  • + + + +
  • Varietates autem iniurasque fortunae facile veteres philosophorum praeceptis instituta vita superabat.
  • + + + +
  • Quamquam tu hanc copiosiorem etiam soles dicere.
  • +
+ \ No newline at end of file diff --git a/test/performance/assets/small-post-with-containers.html b/test/performance/assets/small-post-with-containers.html new file mode 100644 index 00000000000000..f5d32011602dcd --- /dev/null +++ b/test/performance/assets/small-post-with-containers.html @@ -0,0 +1,77 @@ + +
+
+

Heading

+ + + +

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+ + + +
    +
  • one
  • + + + +
  • two
  • + + + +
  • three
  • +
+
+ + + +
+

Heading

+ + + +

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+ + + +
    +
  • one
  • + + + +
  • two
  • + + + +
  • three
  • +
+
+ + + +
+

Heading

+ + + +

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+ + + +
    +
  • one
  • + + + +
  • two
  • + + + +
  • three
  • +
+
+
+ + + +

+ \ No newline at end of file diff --git a/test/performance/config/global-setup.ts b/test/performance/config/global-setup.ts new file mode 100644 index 00000000000000..10f2822fdfe1ae --- /dev/null +++ b/test/performance/config/global-setup.ts @@ -0,0 +1,31 @@ +/** + * External dependencies + */ +import { request } from '@playwright/test'; +import type { FullConfig } from '@playwright/test'; + +/** + * WordPress dependencies + */ +import { RequestUtils } from '@wordpress/e2e-test-utils-playwright'; + +async function globalSetup( config: FullConfig ) { + const { storageState, baseURL } = config.projects[ 0 ].use; + const storageStatePath = + typeof storageState === 'string' ? storageState : undefined; + + const requestContext = await request.newContext( { + baseURL, + } ); + + const requestUtils = new RequestUtils( requestContext, { + storageStatePath, + } ); + + // Authenticate and save the storageState to disk. + await requestUtils.setupRest(); + + await requestContext.dispose(); +} + +export default globalSetup; diff --git a/test/performance/config/performance-reporter.ts b/test/performance/config/performance-reporter.ts new file mode 100644 index 00000000000000..c8865d16cf83f4 --- /dev/null +++ b/test/performance/config/performance-reporter.ts @@ -0,0 +1,174 @@ +/** + * External dependencies + */ +import path from 'path'; +import chalk from 'chalk'; +import { readFileSync, existsSync } from 'fs'; +import type { Reporter, TestCase } from '@playwright/test/reporter'; + +/** + * Internal dependencies + */ +import { average, round } from '../utils'; + +const title = chalk.bold; +const success = chalk.bold.green; + +class PerformanceReporter implements Reporter { + onTestEnd( test: TestCase ) { + const basename = path.basename( test.location.file, '.js' ); + const filepath = path.join( + process.env.WP_ARTIFACTS_PATH as string, + basename + '.performance-results.json' + ); + + if ( ! existsSync( filepath ) ) { + return; + } + + const results = readFileSync( filepath, 'utf8' ); + const { + serverResponse, + firstPaint, + domContentLoaded, + loaded, + firstContentfulPaint, + firstBlock, + type, + typeContainer, + focus, + listViewOpen, + inserterOpen, + inserterHover, + inserterSearch, + } = JSON.parse( results ); + + if ( serverResponse && serverResponse.length ) { + // eslint-disable-next-line no-console + console.log( ` +${ title( 'Loading Time:' ) } +Average time to server response (subtracted from client side metrics): ${ success( + round( average( serverResponse ) ) + 'ms' + ) } +Average time to first paint: ${ success( + round( average( firstPaint ) ) + 'ms' + ) } +Average time to DOM content load: ${ success( + round( average( domContentLoaded ) ) + 'ms' + ) } +Average time to load: ${ success( round( average( loaded ) ) + 'ms' ) } +Average time to first contentful paint: ${ success( + round( average( firstContentfulPaint ) ) + 'ms' + ) } +Average time to first block: ${ success( + round( average( firstBlock ) ) + 'ms' + ) }` ); + } + + if ( type && type.length ) { + // eslint-disable-next-line no-console + console.log( ` +${ title( 'Typing:' ) } +Average time to type character: ${ success( round( average( type ) ) + 'ms' ) } +Slowest time to type character: ${ success( + round( Math.max( ...type ) ) + 'ms' + ) } +Fastest time to type character: ${ success( + round( Math.min( ...type ) ) + 'ms' + ) }` ); + } + + if ( typeContainer && typeContainer.length ) { + // eslint-disable-next-line no-console + console.log( ` +${ title( 'Typing within a container:' ) } +Average time to type within a container: ${ success( + round( average( typeContainer ) ) + 'ms' + ) } +Slowest time to type within a container: ${ success( + round( Math.max( ...typeContainer ) ) + 'ms' + ) } +Fastest time to type within a container: ${ success( + round( Math.min( ...typeContainer ) ) + 'ms' + ) }` ); + } + + if ( focus && focus.length ) { + // eslint-disable-next-line no-console + console.log( ` +${ title( 'Block Selection:' ) } +Average time to select a block: ${ success( round( average( focus ) ) + 'ms' ) } +Slowest time to select a block: ${ success( + round( Math.max( ...focus ) ) + 'ms' + ) } +Fastest time to select a block: ${ success( + round( Math.min( ...focus ) ) + 'ms' + ) }` ); + } + + if ( listViewOpen && listViewOpen.length ) { + // eslint-disable-next-line no-console + console.log( ` +${ title( 'Opening List View:' ) } +Average time to open list view: ${ success( + round( average( listViewOpen ) ) + 'ms' + ) } +Slowest time to open list view: ${ success( + round( Math.max( ...listViewOpen ) ) + 'ms' + ) } +Fastest time to open list view: ${ success( + round( Math.min( ...listViewOpen ) ) + 'ms' + ) }` ); + } + + if ( inserterOpen && inserterOpen.length ) { + // eslint-disable-next-line no-console + console.log( ` +${ title( 'Opening Global Inserter:' ) } +Average time to open global inserter: ${ success( + round( average( inserterOpen ) ) + 'ms' + ) } +Slowest time to open global inserter: ${ success( + round( Math.max( ...inserterOpen ) ) + 'ms' + ) } +Fastest time to open global inserter: ${ success( + round( Math.min( ...inserterOpen ) ) + 'ms' + ) }` ); + } + + if ( inserterSearch && inserterSearch.length ) { + // eslint-disable-next-line no-console + console.log( ` +${ title( 'Inserter Search:' ) } +Average time to type the inserter search input: ${ success( + round( average( inserterSearch ) ) + 'ms' + ) } +Slowest time to type the inserter search input: ${ success( + round( Math.max( ...inserterSearch ) ) + 'ms' + ) } +Fastest time to type the inserter search input: ${ success( + round( Math.min( ...inserterSearch ) ) + 'ms' + ) }` ); + } + + if ( inserterHover && inserterHover.length ) { + // eslint-disable-next-line no-console + console.log( ` +${ title( 'Inserter Block Item Hover:' ) } +Average time to move mouse between two block item in the inserter: ${ success( + round( average( inserterHover ) ) + 'ms' + ) } +Slowest time to move mouse between two block item in the inserter: ${ success( + round( Math.max( ...inserterHover ) ) + 'ms' + ) } +Fastest time to move mouse between two block item in the inserter: ${ success( + round( Math.min( ...inserterHover ) ) + 'ms' + ) }` ); + } + + // eslint-disable-next-line no-console + console.log( '' ); + } +} + +export default PerformanceReporter; diff --git a/test/performance/playwright.config.ts b/test/performance/playwright.config.ts new file mode 100644 index 00000000000000..15053f95d55a73 --- /dev/null +++ b/test/performance/playwright.config.ts @@ -0,0 +1,64 @@ +/** + * External dependencies + */ +import path from 'path'; +import { fileURLToPath } from 'url'; +import { defineConfig, devices } from '@playwright/test'; + +process.env.WP_ARTIFACTS_PATH ??= path.join( process.cwd(), 'artifacts' ); +process.env.STORAGE_STATE_PATH ??= path.join( + process.env.WP_ARTIFACTS_PATH, + 'storage-states/admin.json' +); +process.env.ASSETS_PATH = path.join( __dirname, 'assets' ); + +const config = defineConfig( { + reporter: process.env.CI + ? undefined // We're using another reporter in CI. + : [ [ 'list' ], [ './config/performance-reporter.ts' ] ], + forbidOnly: !! process.env.CI, + fullyParallel: false, + workers: 1, + retries: 0, + timeout: parseInt( process.env.TIMEOUT || '', 10 ) || 600_000, // Defaults to 10 minutes. + testDir: fileURLToPath( new URL( './specs', 'file:' + __filename ).href ), + outputDir: path.join( process.env.WP_ARTIFACTS_PATH, 'test-results' ), + snapshotPathTemplate: + '{testDir}/{testFileDir}/__snapshots__/{arg}-{projectName}{ext}', + globalSetup: fileURLToPath( + new URL( './config/global-setup.ts', 'file:' + __filename ).href + ), + use: { + baseURL: process.env.WP_BASE_URL || 'http://localhost:8889', + headless: true, + viewport: { + width: 960, + height: 700, + }, + ignoreHTTPSErrors: true, + locale: 'en-US', + contextOptions: { + reducedMotion: 'reduce', + strictSelectors: true, + }, + storageState: process.env.STORAGE_STATE_PATH, + actionTimeout: 10_000, // 10 seconds. + trace: 'retain-on-failure', + screenshot: 'only-on-failure', + video: 'off', + }, + webServer: { + command: 'npm run wp-env start', + port: 8889, + timeout: 120_000, // 120 seconds. + reuseExistingServer: true, + }, + projects: [ + { + name: 'chromium', + use: { ...devices[ 'Desktop Chrome' ] }, + }, + ], +} ); + +export default config; diff --git a/test/performance/specs/front-end-block-theme.spec.js b/test/performance/specs/front-end-block-theme.spec.js new file mode 100644 index 00000000000000..93edaa15bdeabd --- /dev/null +++ b/test/performance/specs/front-end-block-theme.spec.js @@ -0,0 +1,76 @@ +/** + * WordPress dependencies + */ +const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); + +/** + * Internal dependencies + */ +const { saveResultsFile } = require( '../utils' ); + +const results = { + timeToFirstByte: [], + largestContentfulPaint: [], + lcpMinusTtfb: [], +}; + +test.describe( 'Front End Performance', () => { + test.beforeAll( async ( { requestUtils } ) => { + await requestUtils.activateTheme( 'twentytwentythree' ); + } ); + + test.afterAll( async ( { requestUtils } ) => { + saveResultsFile( __filename, results ); + await requestUtils.activateTheme( 'twentytwentyone' ); + } ); + + test( 'Report TTFB, LCP, and LCP-TTFB', async ( { page } ) => { + let i = 16; + while ( i-- ) { + // Go to the base URL. + await page.goto( '/', { waitUntil: 'networkidle' } ); + + // Take the measurements. + const [ lcp, ttfb ] = await page.evaluate( () => { + return Promise.all( [ + // Measure the Largest Contentful Paint time. + // Based on https://www.checklyhq.com/learn/headless/basics-performance#largest-contentful-paint-api-largest-contentful-paint + new Promise( ( resolve ) => { + new PerformanceObserver( ( entryList ) => { + const entries = entryList.getEntries(); + // The last entry is the largest contentful paint. + const largestPaintEntry = entries.at( -1 ); + + resolve( largestPaintEntry.startTime ); + } ).observe( { + type: 'largest-contentful-paint', + buffered: true, + } ); + } ), + // Measure the Time To First Byte. + // Based on https://web.dev/ttfb/#measure-ttfb-in-javascript + new Promise( ( resolve ) => { + new PerformanceObserver( ( entryList ) => { + const [ pageNav ] = + entryList.getEntriesByType( 'navigation' ); + + resolve( pageNav.responseStart ); + } ).observe( { + type: 'navigation', + buffered: true, + } ); + } ), + ] ); + } ); + + // Ensure the numbers are valid. + expect( lcp ).toBeGreaterThan( 0 ); + expect( ttfb ).toBeGreaterThan( 0 ); + + // Save the results. + results.largestContentfulPaint.push( lcp ); + results.timeToFirstByte.push( ttfb ); + results.lcpMinusTtfb.push( lcp - ttfb ); + } + } ); +} ); diff --git a/test/performance/specs/front-end-classic-theme.spec.js b/test/performance/specs/front-end-classic-theme.spec.js new file mode 100644 index 00000000000000..2f22f579c1143e --- /dev/null +++ b/test/performance/specs/front-end-classic-theme.spec.js @@ -0,0 +1,75 @@ +/** + * WordPress dependencies + */ +const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); + +/** + * Internal dependencies + */ +const { saveResultsFile } = require( '../utils' ); + +const results = { + timeToFirstByte: [], + largestContentfulPaint: [], + lcpMinusTtfb: [], +}; + +test.describe( 'Front End Performance', () => { + test.beforeAll( async ( { requestUtils } ) => { + await requestUtils.activateTheme( 'twentytwentyone' ); + } ); + + test.afterAll( async () => { + saveResultsFile( __filename, results ); + } ); + + test( 'Measure TTFB, LCP, and LCP-TTFB', async ( { page } ) => { + let i = 16; + while ( i-- ) { + // Go to the base URL. + await page.goto( '/', { waitUntil: 'networkidle' } ); + + // Take the measurements. + const [ lcp, ttfb ] = await page.evaluate( () => { + return Promise.all( [ + // Measure the Largest Contentful Paint time. + // Based on https://www.checklyhq.com/learn/headless/basics-performance#largest-contentful-paint-api-largest-contentful-paint + new Promise( ( resolve ) => { + new PerformanceObserver( ( entryList ) => { + const entries = entryList.getEntries(); + // The last entry is the largest contentful paint. + const largestPaintEntry = entries.at( -1 ); + + resolve( largestPaintEntry.startTime ); + } ).observe( { + type: 'largest-contentful-paint', + buffered: true, + } ); + } ), + // Measure the Time To First Byte. + // Based on https://web.dev/ttfb/#measure-ttfb-in-javascript + new Promise( ( resolve ) => { + new PerformanceObserver( ( entryList ) => { + const [ pageNav ] = + entryList.getEntriesByType( 'navigation' ); + + resolve( pageNav.responseStart ); + } ).observe( { + type: 'navigation', + buffered: true, + } ); + } ), + ] ); + } ); + + // Ensure the numbers are valid. + expect( lcp ).toBeGreaterThan( 0 ); + expect( ttfb ).toBeGreaterThan( 0 ); + + // Save the results. + results.largestContentfulPaint.push( lcp ); + results.timeToFirstByte.push( ttfb ); + results.lcpMinusTtfb.push( lcp - ttfb ); + } + } ); +} ); diff --git a/test/performance/specs/post-editor.spec.js b/test/performance/specs/post-editor.spec.js new file mode 100644 index 00000000000000..9409d51519d74a --- /dev/null +++ b/test/performance/specs/post-editor.spec.js @@ -0,0 +1,390 @@ +/** + * WordPress dependencies + */ +const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); + +/** + * External dependencies + */ +const path = require( 'path' ); + +/** + * Internal dependencies + */ +const { + readFile, + deleteFile, + getTypingEventDurations, + getClickEventDurations, + getHoverEventDurations, + getSelectionEventDurations, + getLoadingDurations, + loadBlocksFromHtml, + load1000Paragraphs, + saveResultsFile, + sum, + getTraceFilePath, +} = require( '../utils' ); + +const results = { + serverResponse: [], + firstPaint: [], + domContentLoaded: [], + loaded: [], + firstContentfulPaint: [], + firstBlock: [], + type: [], + typeContainer: [], + focus: [], + listViewOpen: [], + inserterOpen: [], + inserterHover: [], + inserterSearch: [], +}; + +test.describe( 'Post Editor Performance', () => { + const traceFilePath = getTraceFilePath(); + + test.afterAll( async () => { + saveResultsFile( __filename, results ); + deleteFile( traceFilePath ); + } ); + + test.beforeEach( async ( { admin, page } ) => { + await admin.createNewPost(); + // Disable auto-save to avoid impacting the metrics. + await page.evaluate( () => { + window.wp.data.dispatch( 'core/editor' ).updateEditorSettings( { + autosaveInterval: 100000000000, + localAutosaveInterval: 100000000000, + } ); + } ); + } ); + + test( 'Loading', async ( { browser, page } ) => { + await loadBlocksFromHtml( + page, + path.join( process.env.ASSETS_PATH, 'large-post.html' ) + ); + + await page + .getByRole( 'button', { name: 'Save draft' } ) + .click( { timeout: 60_000 } ); + await expect( + page.getByRole( 'button', { name: 'Saved' } ) + ).toBeDisabled(); + + const draftURL = page.url(); + + // Number of sample measurements to take. + const samples = 5; + // Number of throwaway measurements to perform before recording samples. + // Having at least one helps ensure that caching quirks don't manifest in + // the results. + const throwaway = 1; + + let i = throwaway + samples; + while ( i-- ) { + const testPage = await browser.newPage(); + + await testPage.goto( draftURL ); + await testPage + .frameLocator( 'iframe[name="editor-canvas"]' ) + .locator( '.wp-block' ) + .first() + .waitFor( { + timeout: 120_000, + } ); + + if ( i < samples ) { + const { + serverResponse, + firstPaint, + domContentLoaded, + loaded, + firstContentfulPaint, + firstBlock, + } = await getLoadingDurations( testPage ); + + results.serverResponse.push( serverResponse ); + results.firstPaint.push( firstPaint ); + results.domContentLoaded.push( domContentLoaded ); + results.loaded.push( loaded ); + results.firstContentfulPaint.push( firstContentfulPaint ); + results.firstBlock.push( firstBlock ); + } + + await testPage.close(); + } + } ); + + test( 'Typing', async ( { browser, page, editor } ) => { + await loadBlocksFromHtml( + page, + path.join( process.env.ASSETS_PATH, 'large-post.html' ) + ); + await editor.insertBlock( { name: 'core/paragraph' } ); + + await browser.startTracing( page, { + path: traceFilePath, + screenshots: false, + categories: [ 'devtools.timeline' ], + } ); + + let i = 20; + while ( i-- ) { + // Wait for the browser to be idle before starting the monitoring. + // The timeout should be big enough to allow all async tasks tor run. + // And also to allow Rich Text to mark the change as persistent. + // eslint-disable-next-line no-restricted-syntax + await page.waitForTimeout( 2000 ); + await page.keyboard.type( 'x' ); + } + + await browser.stopTracing(); + const traceResults = JSON.parse( readFile( traceFilePath ) ); + const [ keyDownEvents, keyPressEvents, keyUpEvents ] = + getTypingEventDurations( traceResults ); + if ( + keyDownEvents.length === keyPressEvents.length && + keyPressEvents.length === keyUpEvents.length + ) { + // The first character typed triggers a longer time (isTyping change) + // It can impact the stability of the metric, so we exclude it. + for ( let j = 1; j < keyDownEvents.length; j++ ) { + results.type.push( + keyDownEvents[ j ] + keyPressEvents[ j ] + keyUpEvents[ j ] + ); + } + } + } ); + + test( 'Typing within containers', async ( { browser, page } ) => { + await loadBlocksFromHtml( + page, + path.join( + process.env.ASSETS_PATH, + 'small-post-with-containers.html' + ) + ); + + // Select the block where we type in + await page + .frameLocator( 'iframe[name="editor-canvas"]' ) + .getByRole( 'document', { name: 'Paragraph block' } ) + .first() + .click(); + + // Ignore firsted typed character because it's different + // It probably deserves a dedicated metric. + // (isTyping triggers so it's slower) + await page.keyboard.type( 'x' ); + + await browser.startTracing( page, { + path: traceFilePath, + screenshots: false, + categories: [ 'devtools.timeline' ], + } ); + + let i = 10; + while ( i-- ) { + // Wait for the browser to be idle before starting the monitoring. + // eslint-disable-next-line no-restricted-syntax + await page.waitForTimeout( 500 ); + await page.keyboard.type( 'x' ); + } + // eslint-disable-next-line no-restricted-syntax + await page.waitForTimeout( 500 ); + await browser.stopTracing(); + const traceResults = JSON.parse( readFile( traceFilePath ) ); + const [ keyDownEvents, keyPressEvents, keyUpEvents ] = + getTypingEventDurations( traceResults ); + if ( + keyDownEvents.length === keyPressEvents.length && + keyPressEvents.length === keyUpEvents.length + ) { + // The first character typed triggers a longer time (isTyping change) + // It can impact the stability of the metric, so we exclude it. + for ( let j = 1; j < keyDownEvents.length; j++ ) { + results.typeContainer.push( + keyDownEvents[ j ] + keyPressEvents[ j ] + keyUpEvents[ j ] + ); + } + } + } ); + + test( 'Selecting blocks', async ( { browser, page } ) => { + await load1000Paragraphs( page ); + const paragraphs = page + .frameLocator( 'iframe[name="editor-canvas"]' ) + .locator( '.wp-block' ); + + await paragraphs.first().click(); + + for ( let j = 1; j <= 10; j++ ) { + // Wait for the browser to be idle before starting the monitoring. + // eslint-disable-next-line no-restricted-syntax + await page.waitForTimeout( 1000 ); + await browser.startTracing( page, { + path: traceFilePath, + screenshots: false, + categories: [ 'devtools.timeline' ], + } ); + + await paragraphs.nth( j ).click(); + + await browser.stopTracing(); + const traceResults = JSON.parse( readFile( traceFilePath ) ); + const allDurations = getSelectionEventDurations( traceResults ); + results.focus.push( + allDurations.reduce( ( acc, eventDurations ) => { + return acc + sum( eventDurations ); + }, 0 ) + ); + } + } ); + + test( 'Opening persistent list view', async ( { browser, page } ) => { + await load1000Paragraphs( page ); + const listViewToggle = page.getByRole( 'button', { + name: 'Document Overview', + } ); + + for ( let j = 0; j < 10; j++ ) { + await browser.startTracing( page, { + path: traceFilePath, + screenshots: false, + categories: [ 'devtools.timeline' ], + } ); + + // Open List View + await listViewToggle.click(); + + await browser.stopTracing(); + const traceResults = JSON.parse( readFile( traceFilePath ) ); + const [ mouseClickEvents ] = getClickEventDurations( traceResults ); + for ( let k = 0; k < mouseClickEvents.length; k++ ) { + results.listViewOpen.push( mouseClickEvents[ k ] ); + } + + // Close List View + await listViewToggle.click(); + } + } ); + + test( 'Opening the inserter', async ( { browser, page } ) => { + await load1000Paragraphs( page ); + const globalInserterToggle = page.getByRole( 'button', { + name: 'Toggle block inserter', + } ); + + for ( let j = 0; j < 10; j++ ) { + await browser.startTracing( page, { + path: traceFilePath, + screenshots: false, + categories: [ 'devtools.timeline' ], + } ); + + // Open Inserter. + await globalInserterToggle.click(); + + await browser.stopTracing(); + const traceResults = JSON.parse( readFile( traceFilePath ) ); + const [ mouseClickEvents ] = getClickEventDurations( traceResults ); + for ( let k = 0; k < mouseClickEvents.length; k++ ) { + results.inserterOpen.push( mouseClickEvents[ k ] ); + } + + // Close Inserter. + await globalInserterToggle.click(); + } + } ); + + test( 'Searching the inserter', async ( { browser, page } ) => { + await load1000Paragraphs( page ); + const globalInserterToggle = page.getByRole( 'button', { + name: 'Toggle block inserter', + } ); + + // Open Inserter. + await globalInserterToggle.click(); + + for ( let j = 0; j < 10; j++ ) { + // Wait for the browser to be idle before starting the monitoring. + // eslint-disable-next-line no-restricted-syntax + await page.waitForTimeout( 500 ); + await browser.startTracing( page, { + path: traceFilePath, + screenshots: false, + categories: [ 'devtools.timeline' ], + } ); + await page.keyboard.type( 'p' ); + await browser.stopTracing(); + const traceResults = JSON.parse( readFile( traceFilePath ) ); + const [ keyDownEvents, keyPressEvents, keyUpEvents ] = + getTypingEventDurations( traceResults ); + if ( + keyDownEvents.length === keyPressEvents.length && + keyPressEvents.length === keyUpEvents.length + ) { + results.inserterSearch.push( + sum( keyDownEvents ) + + sum( keyPressEvents ) + + sum( keyUpEvents ) + ); + } + await page.keyboard.press( 'Backspace' ); + } + + // Close Inserter. + await globalInserterToggle.click(); + } ); + + test( 'Hovering Inserter Items', async ( { browser, page } ) => { + await load1000Paragraphs( page ); + const globalInserterToggle = page.getByRole( 'button', { + name: 'Toggle block inserter', + } ); + const paragraphBlockItem = page.locator( + '.block-editor-inserter__menu .editor-block-list-item-paragraph' + ); + const headingBlockItem = page.locator( + '.block-editor-inserter__menu .editor-block-list-item-heading' + ); + + // Open Inserter. + await globalInserterToggle.click(); + + // Hover Items. + await paragraphBlockItem.hover(); + await headingBlockItem.hover(); + + for ( let j = 0; j < 10; j++ ) { + // Wait for the browser to be idle before starting the monitoring. + // eslint-disable-next-line no-restricted-syntax + await page.waitForTimeout( 200 ); + await browser.startTracing( page, { + path: traceFilePath, + screenshots: false, + categories: [ 'devtools.timeline' ], + } ); + + // Hover Items. + await paragraphBlockItem.hover(); + await headingBlockItem.hover(); + + await browser.stopTracing(); + const traceResults = JSON.parse( readFile( traceFilePath ) ); + const [ mouseOverEvents, mouseOutEvents ] = + getHoverEventDurations( traceResults ); + for ( let k = 0; k < mouseOverEvents.length; k++ ) { + results.inserterHover.push( + mouseOverEvents[ k ] + mouseOutEvents[ k ] + ); + } + } + + // Close Inserter. + await globalInserterToggle.click(); + } ); +} ); diff --git a/test/performance/specs/site-editor.spec.js b/test/performance/specs/site-editor.spec.js new file mode 100644 index 00000000000000..85de1ddb8e7691 --- /dev/null +++ b/test/performance/specs/site-editor.spec.js @@ -0,0 +1,206 @@ +/** + * WordPress dependencies + */ +const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); + +/** + * External dependencies + */ +const path = require( 'path' ); + +/** + * Internal dependencies + */ +const { + readFile, + deleteFile, + saveResultsFile, + getTraceFilePath, + getTypingEventDurations, + getLoadingDurations, + loadBlocksFromHtml, +} = require( '../utils' ); + +const results = { + serverResponse: [], + firstPaint: [], + domContentLoaded: [], + loaded: [], + firstContentfulPaint: [], + firstBlock: [], + type: [], + typeContainer: [], + focus: [], + inserterOpen: [], + inserterHover: [], + inserterSearch: [], + listViewOpen: [], +}; + +let testPageId; + +test.describe( 'Site Editor Performance', () => { + test.beforeAll( async ( { requestUtils } ) => { + await requestUtils.activateTheme( 'emptytheme' ); + await requestUtils.deleteAllTemplates( 'wp_template' ); + await requestUtils.deleteAllTemplates( 'wp_template_part' ); + } ); + + test.afterAll( async ( { requestUtils } ) => { + saveResultsFile( __filename, results ); + + await requestUtils.deleteAllTemplates( 'wp_template' ); + await requestUtils.deleteAllTemplates( 'wp_template_part' ); + await requestUtils.activateTheme( 'twentytwentyone' ); + } ); + + test( 'Loading', async ( { browser, page, admin } ) => { + // Start a new page. + await admin.createNewPost( { postType: 'page' } ); + + // Turn the large post HTML into blocks and insert. + await loadBlocksFromHtml( + page, + path.join( process.env.ASSETS_PATH, 'large-post.html' ) + ); + + // Save the draft. + await page + .getByRole( 'button', { name: 'Save draft' } ) + .click( { timeout: 60_000 } ); + await expect( + page.getByRole( 'button', { name: 'Saved' } ) + ).toBeDisabled(); + + // Get the ID of the saved page. + testPageId = await page.evaluate( () => + new URL( document.location ).searchParams.get( 'post' ) + ); + + // Open the test page in Site Editor. + await admin.visitSiteEditor( { + postId: testPageId, + postType: 'page', + } ); + + // Get the URL that we will be testing against. + const targetUrl = page.url(); + + // Start the measurements. + let i = 3; + while ( i-- ) { + // Open a fresh page in a new context to prevent caching. + const testPage = await browser.newPage(); + + // Go to the test page URL. + await testPage.goto( targetUrl ); + + // Wait for the canvas to appear. + await testPage + .locator( '.edit-site-canvas-spinner' ) + .waitFor( { state: 'hidden', timeout: 60_000 } ); + + // Wait for the first block. + await testPage + .frameLocator( 'iframe[name="editor-canvas"]' ) + .locator( '.wp-block' ) + .first() + .waitFor( { timeout: 60_000 } ); + + // Save results. + const { + serverResponse, + firstPaint, + domContentLoaded, + loaded, + firstContentfulPaint, + firstBlock, + } = await getLoadingDurations( testPage ); + results.serverResponse.push( serverResponse ); + results.firstPaint.push( firstPaint ); + results.domContentLoaded.push( domContentLoaded ); + results.loaded.push( loaded ); + results.firstContentfulPaint.push( firstContentfulPaint ); + results.firstBlock.push( firstBlock ); + + await testPage.close(); + } + } ); + + test( 'Typing', async ( { browser, page, pageUtils, admin } ) => { + // Start a new page. + await admin.createNewPost( { postType: 'page' } ); + + // Turn the large post HTML into blocks and insert. + await loadBlocksFromHtml( + page, + path.join( process.env.ASSETS_PATH, 'large-post.html' ) + ); + + // Save the draft. + await page + .getByRole( 'button', { name: 'Save draft' } ) + // Loading the large post HTML can take some time so we need a higher + // timeout value here. + .click( { timeout: 60_000 } ); + await expect( + page.getByRole( 'button', { name: 'Saved' } ) + ).toBeDisabled(); + + // Get the ID of the saved page. + testPageId = await page.evaluate( () => + new URL( document.location ).searchParams.get( 'post' ) + ); + + // Open the test page in Site Editor. + await admin.visitSiteEditor( { + postId: testPageId, + postType: 'page', + } ); + + // Wait for the first paragraph to be ready. + const canvas = page.frameLocator( 'iframe[name="editor-canvas"]' ); + const firstParagraph = canvas + .getByText( 'Lorem ipsum dolor sit amet' ) + .first(); + await firstParagraph.waitFor( { timeout: 60_000 } ); + + // Enter edit mode. + await canvas.locator( 'body' ).click(); + + // Insert a new paragraph right under the first one. + await canvas + .getByRole( 'document', { name: 'Block: Post Content' } ) + .click(); + await firstParagraph.click(); + await pageUtils.pressKeys( 'primary+a' ); + await page.keyboard.press( 'ArrowRight' ); + await page.keyboard.press( 'Enter' ); + + // Start tracing. + const traceFilePath = getTraceFilePath(); + await browser.startTracing( page, { + path: traceFilePath, + screenshots: false, + categories: [ 'devtools.timeline' ], + } ); + + // Type "x" 200 times. + const typingSequence = new Array( 200 ).fill( 'x' ).join( '' ); + await page.keyboard.type( typingSequence ); + + // Stop tracing and save results. + await browser.stopTracing(); + const traceResults = JSON.parse( readFile( traceFilePath ) ); + const [ keyDownEvents, keyPressEvents, keyUpEvents ] = + getTypingEventDurations( traceResults ); + for ( let i = 0; i < keyDownEvents.length; i++ ) { + results.type.push( + keyDownEvents[ i ] + keyPressEvents[ i ] + keyUpEvents[ i ] + ); + } + + // Delete the original trace file. + deleteFile( traceFilePath ); + } ); +} ); diff --git a/test/performance/tsconfig.json b/test/performance/tsconfig.json new file mode 100644 index 00000000000000..7f855fd0ba69c8 --- /dev/null +++ b/test/performance/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "noEmit": true, + "emitDeclarationOnly": false, + "allowJs": true, + "checkJs": false + }, + "include": [ "**/*" ], + "exclude": [] +} diff --git a/test/performance/utils.js b/test/performance/utils.js new file mode 100644 index 00000000000000..e7bbb4206ee8d2 --- /dev/null +++ b/test/performance/utils.js @@ -0,0 +1,183 @@ +/** + * External dependencies + */ +import path from 'path'; +import { existsSync, readFileSync, unlinkSync, writeFileSync } from 'fs'; + +export function readFile( filePath ) { + return existsSync( filePath ) + ? readFileSync( filePath, 'utf8' ).trim() + : ''; +} + +export function deleteFile( filePath ) { + if ( existsSync( filePath ) ) { + unlinkSync( filePath ); + } +} + +export function getTraceFilePath() { + return path.join( process.env.WP_ARTIFACTS_PATH, '/trace.json' ); +} + +export function saveResultsFile( testFilename, results ) { + const resultsFilename = + process.env.RESULTS_FILENAME || + path.basename( testFilename, '.js' ) + '.performance-results.json'; + + return writeFileSync( + path.join( process.env.WP_ARTIFACTS_PATH, resultsFilename ), + JSON.stringify( results, null, 2 ) + ); +} + +function isEvent( item ) { + return ( + item.cat === 'devtools.timeline' && + item.name === 'EventDispatch' && + item.dur && + item.args && + item.args.data + ); +} + +function isKeyDownEvent( item ) { + return isEvent( item ) && item.args.data.type === 'keydown'; +} + +function isKeyPressEvent( item ) { + return isEvent( item ) && item.args.data.type === 'keypress'; +} + +function isKeyUpEvent( item ) { + return isEvent( item ) && item.args.data.type === 'keyup'; +} + +function isFocusEvent( item ) { + return isEvent( item ) && item.args.data.type === 'focus'; +} + +function isFocusInEvent( item ) { + return isEvent( item ) && item.args.data.type === 'focusin'; +} + +function isClickEvent( item ) { + return isEvent( item ) && item.args.data.type === 'click'; +} + +function isMouseOverEvent( item ) { + return isEvent( item ) && item.args.data.type === 'mouseover'; +} + +function isMouseOutEvent( item ) { + return isEvent( item ) && item.args.data.type === 'mouseout'; +} + +function getEventDurationsForType( trace, filterFunction ) { + return trace.traceEvents + .filter( filterFunction ) + .map( ( item ) => item.dur / 1000 ); +} + +export function getTypingEventDurations( trace ) { + return [ + getEventDurationsForType( trace, isKeyDownEvent ), + getEventDurationsForType( trace, isKeyPressEvent ), + getEventDurationsForType( trace, isKeyUpEvent ), + ]; +} + +export function getSelectionEventDurations( trace ) { + return [ + getEventDurationsForType( trace, isFocusEvent ), + getEventDurationsForType( trace, isFocusInEvent ), + ]; +} + +export function getClickEventDurations( trace ) { + return [ getEventDurationsForType( trace, isClickEvent ) ]; +} + +export function getHoverEventDurations( trace ) { + return [ + getEventDurationsForType( trace, isMouseOverEvent ), + getEventDurationsForType( trace, isMouseOutEvent ), + ]; +} + +export async function getLoadingDurations( page ) { + return await page.evaluate( () => { + const [ + { + requestStart, + responseStart, + responseEnd, + domContentLoadedEventEnd, + loadEventEnd, + }, + ] = performance.getEntriesByType( 'navigation' ); + const paintTimings = performance.getEntriesByType( 'paint' ); + return { + // Server side metric. + serverResponse: responseStart - requestStart, + // For client side metrics, consider the end of the response (the + // browser receives the HTML) as the start time (0). + firstPaint: + paintTimings.find( ( { name } ) => name === 'first-paint' ) + .startTime - responseEnd, + domContentLoaded: domContentLoadedEventEnd - responseEnd, + loaded: loadEventEnd - responseEnd, + firstContentfulPaint: + paintTimings.find( + ( { name } ) => name === 'first-contentful-paint' + ).startTime - responseEnd, + // This is evaluated right after Playwright found the block selector. + firstBlock: performance.now() - responseEnd, + }; + } ); +} + +export function sum( arr ) { + return arr.reduce( ( a, b ) => a + b, 0 ); +} + +export function average( array ) { + return array.reduce( ( a, b ) => a + b ) / array.length; +} + +export function round( number, decimalPlaces = 2 ) { + const factor = Math.pow( 10, decimalPlaces ); + return Math.round( number * factor ) / factor; +} + +export async function loadBlocksFromHtml( page, filepath ) { + if ( ! existsSync( filepath ) ) { + throw new Error( `File not found (${ filepath })` ); + } + + return await page.evaluate( ( html ) => { + const { parse } = window.wp.blocks; + const { dispatch } = window.wp.data; + const blocks = parse( html ); + + blocks.forEach( ( block ) => { + if ( block.name === 'core/image' ) { + delete block.attributes.id; + delete block.attributes.url; + } + } ); + + dispatch( 'core/block-editor' ).resetBlocks( blocks ); + }, readFile( filepath ) ); +} + +export async function load1000Paragraphs( page ) { + await page.evaluate( () => { + const { createBlock } = window.wp.blocks; + const { dispatch } = window.wp.data; + const blocks = Array.from( { length: 1000 } ).map( () => + createBlock( 'core/paragraph' ) + ); + dispatch( 'core/block-editor' ).resetBlocks( blocks ); + } ); +} From 2aa50e73a695521790edeccadcbae99cb3596dad Mon Sep 17 00:00:00 2001 From: annezazu Date: Tue, 27 Jun 2023 09:13:48 -0700 Subject: [PATCH 039/266] Update versions in WP for 6.3 (#51984) --- docs/contributors/versions-in-wordpress.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/contributors/versions-in-wordpress.md b/docs/contributors/versions-in-wordpress.md index 8e1e471522aa77..6709d124b242b8 100644 --- a/docs/contributors/versions-in-wordpress.md +++ b/docs/contributors/versions-in-wordpress.md @@ -6,6 +6,7 @@ If anything looks incorrect here, please bring it up in #core-editor in [WordPre | Gutenberg Versions | WordPress Version | | ------------------ | ----------------- | +| 15.2-16.1 | 6.3 | | 14.2-15.1 | 6.2 | | 13.1-14.1 | 6.1.1 | | 13.1-14.1 | 6.1 | From c711e2aa613aceb93836635e854badd8ac15310d Mon Sep 17 00:00:00 2001 From: Ramon Date: Wed, 28 Jun 2023 14:05:36 +1000 Subject: [PATCH 040/266] Updating social link attributes (#51997) --- packages/block-library/src/social-link/index.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/block-library/src/social-link/index.php b/packages/block-library/src/social-link/index.php index 51ed9374c9bcdf..1e5c4cf5de6fbd 100644 --- a/packages/block-library/src/social-link/index.php +++ b/packages/block-library/src/social-link/index.php @@ -47,8 +47,8 @@ function render_block_core_social_link( $attributes, $content, $block ) { $icon = block_core_social_link_get_icon( $service ); $wrapper_attributes = get_block_wrapper_attributes( array( - 'class' => 'wp-social-link wp-social-link-' . $service . block_core_social_link_get_color_classes( $block->context ), - 'style' => block_core_social_link_get_color_styles( $block->context ), + 'class' => esc_attr( 'wp-social-link wp-social-link-' . $service . block_core_social_link_get_color_classes( $block->context ) ), + 'style' => esc_attr( block_core_social_link_get_color_styles( $block->context ) ), ) ); From e34d508183e8ca5f3de1a5293daf4da38ffba56b Mon Sep 17 00:00:00 2001 From: Glen Davies Date: Wed, 28 Jun 2023 18:33:07 +1200 Subject: [PATCH 041/266] Fix custom patterns console error (#51947) * Fix error with react list key with new custom patterns list in inserter * Update placeholder key * Add comment to explain the different keys * Patterns: Fix missing custom patterns in patterns explorer (#51889) * Add custom patterns to pattern explorer * show custom patterns in the patterns explorer dialog * remove changes from 51877 * Fix up use of async lists * remove a bit of code duplication by adding a new hook * add 51877 fix back to make testing easier * Just assign the key value in one place * Refactor the custom patterns to use the usePatternsState hook * Fix use of async list * Translate strings and remove unneeded fields from pattern object * Try integrating unsynced patterns directly into pattern selectors (#51955) * Include reusable blocks with an undefined sync status in inserter items * Update docs * Remove change to hover dependencies --------- Co-authored-by: Daniel Richards --- .../data/data-core-block-editor.md | 1 - .../block-patterns-explorer/patterns-list.js | 10 ++- .../components/inserter/block-patterns-tab.js | 63 ++-------------- .../inserter/hooks/use-block-types-state.js | 7 +- .../inserter/hooks/use-patterns-state.js | 54 +++++++++----- packages/block-editor/src/store/selectors.js | 71 +++++++++++++------ 6 files changed, 104 insertions(+), 102 deletions(-) diff --git a/docs/reference-guides/data/data-core-block-editor.md b/docs/reference-guides/data/data-core-block-editor.md index cbf777d68429d5..67dbc48ccf29d8 100644 --- a/docs/reference-guides/data/data-core-block-editor.md +++ b/docs/reference-guides/data/data-core-block-editor.md @@ -538,7 +538,6 @@ _Parameters_ - _state_ `Object`: Editor state. - _rootClientId_ `?string`: Optional root client ID of block list. -- _syncStatus_ `?string`: Optional sync status to filter pattern blocks by. _Returns_ diff --git a/packages/block-editor/src/components/inserter/block-patterns-explorer/patterns-list.js b/packages/block-editor/src/components/inserter/block-patterns-explorer/patterns-list.js index a4e3c91cba4ba2..fda1a00c1a07dc 100644 --- a/packages/block-editor/src/components/inserter/block-patterns-explorer/patterns-list.js +++ b/packages/block-editor/src/components/inserter/block-patterns-explorer/patterns-list.js @@ -52,6 +52,7 @@ function PatternList( { filterValue, selectedCategory, patternCategories } ) { onInsertBlocks, destinationRootClientId ); + const registeredPatternCategories = useMemo( () => patternCategories.map( @@ -75,7 +76,12 @@ function PatternList( { filterValue, selectedCategory, patternCategories } ) { ); } return searchItems( allPatterns, filterValue ); - }, [ filterValue, selectedCategory, allPatterns ] ); + }, [ + filterValue, + allPatterns, + selectedCategory, + registeredPatternCategories, + ] ); // Announce search results on change. useEffect( () => { @@ -89,7 +95,7 @@ function PatternList( { filterValue, selectedCategory, patternCategories } ) { count ); debouncedSpeak( resultsFoundMessage ); - }, [ filterValue, debouncedSpeak ] ); + }, [ filterValue, debouncedSpeak, filteredBlockPatterns.length ] ); const currentShownPatterns = useAsyncList( filteredBlockPatterns, { step: INITIAL_INSERTER_RESULTS, diff --git a/packages/block-editor/src/components/inserter/block-patterns-tab.js b/packages/block-editor/src/components/inserter/block-patterns-tab.js index 578791e8802692..5c2720fd0502e3 100644 --- a/packages/block-editor/src/components/inserter/block-patterns-tab.js +++ b/packages/block-editor/src/components/inserter/block-patterns-tab.js @@ -18,7 +18,6 @@ import { Button, } from '@wordpress/components'; import { Icon, chevronRight, chevronLeft } from '@wordpress/icons'; -import { parse } from '@wordpress/blocks'; import { focus } from '@wordpress/dom'; /** @@ -28,7 +27,6 @@ import usePatternsState from './hooks/use-patterns-state'; import BlockPatternList from '../block-patterns-list'; import PatternsExplorerModal from './block-patterns-explorer/explorer'; import MobileTabNavigation from './mobile-tab-navigation'; -import useBlockTypesState from './hooks/use-block-types-state'; const noop = () => {}; @@ -51,18 +49,6 @@ function usePatternsCategories( rootClientId ) { rootClientId ); - const [ unsyncedPatterns ] = useBlockTypesState( - rootClientId, - undefined, - 'unsynced' - ); - - const filteredUnsyncedPatterns = useMemo( () => { - return unsyncedPatterns.filter( - ( { category: unsyncedPatternCategory } ) => - unsyncedPatternCategory === 'reusable' - ); - }, [ unsyncedPatterns ] ); const hasRegisteredCategory = useCallback( ( pattern ) => { if ( ! pattern.categories || ! pattern.categories.length ) { @@ -107,20 +93,9 @@ function usePatternsCategories( rootClientId ) { label: _x( 'Uncategorized' ), } ); } - if ( filteredUnsyncedPatterns.length > 0 ) { - categories.push( { - name: 'reusable', - label: _x( 'Custom patterns' ), - } ); - } return categories; - }, [ - allCategories, - allPatterns, - filteredUnsyncedPatterns.length, - hasRegisteredCategory, - ] ); + }, [ allCategories, allPatterns, hasRegisteredCategory ] ); return populatedCategories; } @@ -169,24 +144,6 @@ export function BlockPatternsCategoryPanel( { onInsert, rootClientId ); - const [ unsyncedPatterns ] = useBlockTypesState( - rootClientId, - onInsert, - 'unsynced' - ); - const filteredUnsyncedPatterns = useMemo( () => { - return unsyncedPatterns - .filter( - ( { category: unsyncedPatternCategory } ) => - unsyncedPatternCategory === 'reusable' - ) - .map( ( syncedPattern ) => ( { - ...syncedPattern, - blocks: parse( syncedPattern.content, { - __unstableSkipMigrationLogs: true, - } ), - } ) ); - }, [ unsyncedPatterns ] ); const availableCategories = usePatternsCategories( rootClientId ); const currentCategoryPatterns = useMemo( @@ -208,21 +165,15 @@ export function BlockPatternsCategoryPanel( { return availablePatternCategories.length === 0; } ), - [ allPatterns, category ] + [ allPatterns, availableCategories, category.name ] ); - const patterns = - category.name === 'reusable' - ? filteredUnsyncedPatterns - : currentCategoryPatterns; - const currentShownPatterns = useAsyncList( patterns ); + + const categoryPatternsList = useAsyncList( currentCategoryPatterns ); // Hide block pattern preview on unmount. useEffect( () => () => onHover( null ), [] ); - if ( - ! currentCategoryPatterns.length && - ! filteredUnsyncedPatterns.length - ) { + if ( ! currentCategoryPatterns.length ) { return null; } @@ -233,8 +184,8 @@ export function BlockPatternsCategoryPanel( {

{ category.description }

{ +const useBlockTypesState = ( rootClientId, onInsert ) => { const { categories, collections, items } = useSelect( ( select ) => { const { getInserterItems } = select( blockEditorStore ); @@ -31,10 +30,10 @@ const useBlockTypesState = ( rootClientId, onInsert, syncStatus ) => { return { categories: getCategories(), collections: getCollections(), - items: getInserterItems( rootClientId, syncStatus ), + items: getInserterItems( rootClientId ), }; }, - [ rootClientId, syncStatus ] + [ rootClientId ] ); const onSelectItem = useCallback( diff --git a/packages/block-editor/src/components/inserter/hooks/use-patterns-state.js b/packages/block-editor/src/components/inserter/hooks/use-patterns-state.js index ca287b95c43b9b..2a99e637ed1237 100644 --- a/packages/block-editor/src/components/inserter/hooks/use-patterns-state.js +++ b/packages/block-editor/src/components/inserter/hooks/use-patterns-state.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { useCallback } from '@wordpress/element'; +import { useCallback, useMemo } from '@wordpress/element'; import { cloneBlock } from '@wordpress/blocks'; import { useDispatch, useSelect } from '@wordpress/data'; import { __, sprintf } from '@wordpress/i18n'; @@ -12,6 +12,12 @@ import { store as noticesStore } from '@wordpress/notices'; */ import { store as blockEditorStore } from '../../../store'; +const CUSTOM_CATEGORY = { + name: 'custom', + label: __( 'Custom patterns' ), + description: __( 'Custom patterns add by site users' ), +}; + /** * Retrieves the block patterns inserter state. * @@ -25,6 +31,7 @@ const usePatternsState = ( onInsert, rootClientId ) => { ( select ) => { const { __experimentalGetAllowedPatterns, getSettings } = select( blockEditorStore ); + return { patterns: __experimentalGetAllowedPatterns( rootClientId ), patternCategories: @@ -33,25 +40,34 @@ const usePatternsState = ( onInsert, rootClientId ) => { }, [ rootClientId ] ); + + const allCategories = useMemo( + () => [ ...patternCategories, CUSTOM_CATEGORY ], + [ patternCategories ] + ); + const { createSuccessNotice } = useDispatch( noticesStore ); - const onClickPattern = useCallback( ( pattern, blocks ) => { - onInsert( - ( blocks ?? [] ).map( ( block ) => cloneBlock( block ) ), - pattern.name - ); - createSuccessNotice( - sprintf( - /* translators: %s: block pattern title. */ - __( 'Block pattern "%s" inserted.' ), - pattern.title - ), - { - type: 'snackbar', - } - ); - }, [] ); - - return [ patterns, patternCategories, onClickPattern ]; + const onClickPattern = useCallback( + ( pattern, blocks ) => { + onInsert( + ( blocks ?? [] ).map( ( block ) => cloneBlock( block ) ), + pattern.name + ); + createSuccessNotice( + sprintf( + /* translators: %s: block pattern title. */ + __( 'Block pattern "%s" inserted.' ), + pattern.title + ), + { + type: 'snackbar', + } + ); + }, + [ createSuccessNotice, onInsert ] + ); + + return [ patterns, allCategories, onClickPattern ]; }; export default usePatternsState; diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 05d00f65b1fd34..fc314636d11951 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -1945,7 +1945,6 @@ const buildBlockTypeItem = * * @param {Object} state Editor state. * @param {?string} rootClientId Optional root client ID of block list. - * @param {?string} syncStatus Optional sync status to filter pattern blocks by. * * @return {WPEditorInserterItem[]} Items that appear in inserter. * @@ -1962,11 +1961,7 @@ const buildBlockTypeItem = * @property {number} frecency Heuristic that combines frequency and recency. */ export const getInserterItems = createSelector( - ( state, rootClientId = null, syncStatus ) => { - const buildBlockTypeInserterItem = buildBlockTypeItem( state, { - buildScope: 'inserter', - } ); - + ( state, rootClientId = null ) => { /* * Matches block comment delimiters amid serialized content. * @@ -2031,13 +2026,7 @@ export const getInserterItems = createSelector( }; }; - const blockTypeInserterItems = getBlockTypes() - .filter( ( blockType ) => - canIncludeBlockTypeInInserter( state, blockType, rootClientId ) - ) - .map( buildBlockTypeInserterItem ); - - const reusableBlockInserterItems = canInsertBlockTypeUnmemoized( + const syncedPatternInserterItems = canInsertBlockTypeUnmemoized( state, 'core/block', rootClientId @@ -2045,13 +2034,25 @@ export const getInserterItems = createSelector( ? getReusableBlocks( state ) .filter( ( reusableBlock ) => - syncStatus === reusableBlock.meta?.sync_status || - ( ! syncStatus && - reusableBlock.meta?.sync_status === '' ) + // Filter to either fully synced patterns (sync_status === 'fully'), + // or old school reusable blocks (sync_status === ''). + reusableBlock.meta?.sync_status === 'fully' || + reusableBlock.meta?.sync_status === '' || + ! reusableBlock.meta?.sync_status ) .map( buildReusableBlockInserterItem ) : []; + const buildBlockTypeInserterItem = buildBlockTypeItem( state, { + buildScope: 'inserter', + } ); + + const blockTypeInserterItems = getBlockTypes() + .filter( ( blockType ) => + canIncludeBlockTypeInInserter( state, blockType, rootClientId ) + ) + .map( buildBlockTypeInserterItem ); + const items = blockTypeInserterItems.reduce( ( accumulator, item ) => { const { variations = [] } = item; // Exclude any block type item that is to be replaced by a default variation. @@ -2082,7 +2083,7 @@ export const getInserterItems = createSelector( { core: [], noncore: [] } ); const sortedBlockTypes = [ ...coreItems, ...nonCoreItems ]; - return [ ...sortedBlockTypes, ...reusableBlockInserterItems ]; + return [ ...sortedBlockTypes, ...syncedPatternInserterItems ]; }, ( state, rootClientId ) => [ state.blockListSettings[ rootClientId ], @@ -2306,10 +2307,32 @@ const checkAllowListRecursive = ( blocks, allowedBlockTypes ) => { return true; }; +function getUnsyncedPatterns( state ) { + const reusableBlocks = + state?.settings?.__experimentalReusableBlocks ?? EMPTY_ARRAY; + + return reusableBlocks + .filter( + ( reusableBlock ) => reusableBlock.meta?.sync_status === 'unsynced' + ) + .map( ( reusableBlock ) => { + return { + name: `core/block/${ reusableBlock.id }`, + title: reusableBlock.title.raw, + categories: [ 'custom' ], + content: reusableBlock.content.raw, + }; + } ); +} + export const __experimentalGetParsedPattern = createSelector( ( state, patternName ) => { const patterns = state.settings.__experimentalBlockPatterns; - const pattern = patterns.find( ( { name } ) => name === patternName ); + const unsyncedPatterns = getUnsyncedPatterns( state ); + + const pattern = [ ...patterns, ...unsyncedPatterns ].find( + ( { name } ) => name === patternName + ); if ( ! pattern ) { return null; } @@ -2320,14 +2343,20 @@ export const __experimentalGetParsedPattern = createSelector( } ), }; }, - ( state ) => [ state.settings.__experimentalBlockPatterns ] + ( state ) => [ + state.settings.__experimentalBlockPatterns, + state.settings.__experimentalReusableBlocks, + ] ); const getAllAllowedPatterns = createSelector( ( state ) => { const patterns = state.settings.__experimentalBlockPatterns; + const unsyncedPatterns = getUnsyncedPatterns( state ); + const { allowedBlockTypes } = getSettings( state ); - const parsedPatterns = patterns + + const parsedPatterns = [ ...patterns, ...unsyncedPatterns ] .filter( ( { inserter = true } ) => !! inserter ) .map( ( { name } ) => __experimentalGetParsedPattern( state, name ) @@ -2339,6 +2368,7 @@ const getAllAllowedPatterns = createSelector( }, ( state ) => [ state.settings.__experimentalBlockPatterns, + state.settings.__experimentalReusableBlocks, state.settings.allowedBlockTypes, ] ); @@ -2365,6 +2395,7 @@ export const __experimentalGetAllowedPatterns = createSelector( }, ( state, rootClientId ) => [ state.settings.__experimentalBlockPatterns, + state.settings.__experimentalReusableBlocks, state.settings.allowedBlockTypes, state.settings.templateLock, state.blockListSettings[ rootClientId ], From 52d1b8d0c407042e0517df7d04d104c5eea7f59a Mon Sep 17 00:00:00 2001 From: Gutenberg Repository Automation Date: Wed, 28 Jun 2023 06:43:46 +0000 Subject: [PATCH 042/266] Bump plugin version to 16.1.0 --- gutenberg.php | 2 +- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gutenberg.php b/gutenberg.php index abeab01e72976f..f0c0bf22545b1a 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.1 * Requires PHP: 5.6 - * Version: 16.1.0-rc.2 + * Version: 16.1.0 * Author: Gutenberg Team * Text Domain: gutenberg * diff --git a/package-lock.json b/package-lock.json index 4de1bfc12b7036..d6c531c2605f1c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "16.1.0-rc.2", + "version": "16.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 8910f972fe91b0..31c2e68a6af299 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "16.1.0-rc.2", + "version": "16.1.0", "private": true, "description": "A new WordPress editor experience.", "author": "The WordPress Contributors", From af39e943d6755979de76f78d86f7a60f93336e71 Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Wed, 28 Jun 2023 16:16:17 +0900 Subject: [PATCH 043/266] Heading Block: Remove unused `HeadingLevelIcon` component (#52008) --- .../src/heading/heading-level-icon.js | 48 ------------------- 1 file changed, 48 deletions(-) delete mode 100644 packages/block-library/src/heading/heading-level-icon.js diff --git a/packages/block-library/src/heading/heading-level-icon.js b/packages/block-library/src/heading/heading-level-icon.js deleted file mode 100644 index b3288d02761612..00000000000000 --- a/packages/block-library/src/heading/heading-level-icon.js +++ /dev/null @@ -1,48 +0,0 @@ -/** - * WordPress dependencies - */ -import { Path, SVG } from '@wordpress/components'; - -/** @typedef {import('@wordpress/element').WPComponent} WPComponent */ - -/** - * HeadingLevelIcon props. - * - * @typedef WPHeadingLevelIconProps - * - * @property {number} level The heading level to show an icon for. - * @property {?boolean} isPressed Whether or not the icon should appear pressed; default: false. - */ - -/** - * Heading level icon. - * - * @param {WPHeadingLevelIconProps} props Component props. - * - * @return {?WPComponent} The icon. - */ -export default function HeadingLevelIcon( { level, isPressed = false } ) { - const levelToPath = { - 1: 'M9 5h2v10H9v-4H5v4H3V5h2v4h4V5zm6.6 0c-.6.9-1.5 1.7-2.6 2v1h2v7h2V5h-1.4z', - 2: 'M7 5h2v10H7v-4H3v4H1V5h2v4h4V5zm8 8c.5-.4.6-.6 1.1-1.1.4-.4.8-.8 1.2-1.3.3-.4.6-.8.9-1.3.2-.4.3-.8.3-1.3 0-.4-.1-.9-.3-1.3-.2-.4-.4-.7-.8-1-.3-.3-.7-.5-1.2-.6-.5-.2-1-.2-1.5-.2-.4 0-.7 0-1.1.1-.3.1-.7.2-1 .3-.3.1-.6.3-.9.5-.3.2-.6.4-.8.7l1.2 1.2c.3-.3.6-.5 1-.7.4-.2.7-.3 1.2-.3s.9.1 1.3.4c.3.3.5.7.5 1.1 0 .4-.1.8-.4 1.1-.3.5-.6.9-1 1.2-.4.4-1 .9-1.6 1.4-.6.5-1.4 1.1-2.2 1.6V15h8v-2H15z', - 3: 'M12.1 12.2c.4.3.8.5 1.2.7.4.2.9.3 1.4.3.5 0 1-.1 1.4-.3.3-.1.5-.5.5-.8 0-.2 0-.4-.1-.6-.1-.2-.3-.3-.5-.4-.3-.1-.7-.2-1-.3-.5-.1-1-.1-1.5-.1V9.1c.7.1 1.5-.1 2.2-.4.4-.2.6-.5.6-.9 0-.3-.1-.6-.4-.8-.3-.2-.7-.3-1.1-.3-.4 0-.8.1-1.1.3-.4.2-.7.4-1.1.6l-1.2-1.4c.5-.4 1.1-.7 1.6-.9.5-.2 1.2-.3 1.8-.3.5 0 1 .1 1.6.2.4.1.8.3 1.2.5.3.2.6.5.8.8.2.3.3.7.3 1.1 0 .5-.2.9-.5 1.3-.4.4-.9.7-1.5.9v.1c.6.1 1.2.4 1.6.8.4.4.7.9.7 1.5 0 .4-.1.8-.3 1.2-.2.4-.5.7-.9.9-.4.3-.9.4-1.3.5-.5.1-1 .2-1.6.2-.8 0-1.6-.1-2.3-.4-.6-.2-1.1-.6-1.6-1l1.1-1.4zM7 9H3V5H1v10h2v-4h4v4h2V5H7v4z', - 4: 'M9 15H7v-4H3v4H1V5h2v4h4V5h2v10zm10-2h-1v2h-2v-2h-5v-2l4-6h3v6h1v2zm-3-2V7l-2.8 4H16z', - 5: 'M12.1 12.2c.4.3.7.5 1.1.7.4.2.9.3 1.3.3.5 0 1-.1 1.4-.4.4-.3.6-.7.6-1.1 0-.4-.2-.9-.6-1.1-.4-.3-.9-.4-1.4-.4H14c-.1 0-.3 0-.4.1l-.4.1-.5.2-1-.6.3-5h6.4v1.9h-4.3L14 8.8c.2-.1.5-.1.7-.2.2 0 .5-.1.7-.1.5 0 .9.1 1.4.2.4.1.8.3 1.1.6.3.2.6.6.8.9.2.4.3.9.3 1.4 0 .5-.1 1-.3 1.4-.2.4-.5.8-.9 1.1-.4.3-.8.5-1.3.7-.5.2-1 .3-1.5.3-.8 0-1.6-.1-2.3-.4-.6-.2-1.1-.6-1.6-1-.1-.1 1-1.5 1-1.5zM9 15H7v-4H3v4H1V5h2v4h4V5h2v10z', - 6: 'M9 15H7v-4H3v4H1V5h2v4h4V5h2v10zm8.6-7.5c-.2-.2-.5-.4-.8-.5-.6-.2-1.3-.2-1.9 0-.3.1-.6.3-.8.5l-.6.9c-.2.5-.2.9-.2 1.4.4-.3.8-.6 1.2-.8.4-.2.8-.3 1.3-.3.4 0 .8 0 1.2.2.4.1.7.3 1 .6.3.3.5.6.7.9.2.4.3.8.3 1.3s-.1.9-.3 1.4c-.2.4-.5.7-.8 1-.4.3-.8.5-1.2.6-1 .3-2 .3-3 0-.5-.2-1-.5-1.4-.9-.4-.4-.8-.9-1-1.5-.2-.6-.3-1.3-.3-2.1s.1-1.6.4-2.3c.2-.6.6-1.2 1-1.6.4-.4.9-.7 1.4-.9.6-.3 1.1-.4 1.7-.4.7 0 1.4.1 2 .3.5.2 1 .5 1.4.8 0 .1-1.3 1.4-1.3 1.4zm-2.4 5.8c.2 0 .4 0 .6-.1.2 0 .4-.1.5-.2.1-.1.3-.3.4-.5.1-.2.1-.5.1-.7 0-.4-.1-.8-.4-1.1-.3-.2-.7-.3-1.1-.3-.3 0-.7.1-1 .2-.4.2-.7.4-1 .7 0 .3.1.7.3 1 .1.2.3.4.4.6.2.1.3.3.5.3.2.1.5.2.7.1z', - }; - if ( ! levelToPath.hasOwnProperty( level ) ) { - return null; - } - - return ( - - - - ); -} From 737a8a128ccc1ff5da28fb0e14cf092214fa206e Mon Sep 17 00:00:00 2001 From: Gutenberg Repository Automation Date: Wed, 28 Jun 2023 07:36:19 +0000 Subject: [PATCH 044/266] Update Changelog for 16.1.0 --- changelog.txt | 132 +++++++++++++++++++------------------------------- 1 file changed, 51 insertions(+), 81 deletions(-) diff --git a/changelog.txt b/changelog.txt index b392837ad2d2cd..277ac5f08ffb82 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,77 +1,6 @@ == Changelog == -= 16.1.0-rc.2 = - -## Changelog - -### Enhancements - -#### Block Editor -- Improve LinkControl Edit UI. ([51712](https://github.com/WordPress/gutenberg/pull/51712)) - -#### Site Editor -- Adding the distraction-free mode to the site editor (#51173). ([51932](https://github.com/WordPress/gutenberg/pull/51932)) -- Update colors in 'Site view'. ([51856](https://github.com/WordPress/gutenberg/pull/51856)) -- Site Editor: Make string to add Template parts and Patterns consistent and translatable [51743](https://github.com/WordPress/gutenberg/pull/51743) - -#### Block Library -- Add image block aspect ratio control [51545](https://github.com/WordPress/gutenberg/pull/51545) - -#### Components -- Button: Introduce size prop [51842](https://github.com/WordPress/gutenberg/pull/51842) - -#### Navigation -- Ensure there is always a Navigation available in the browse mode sidebar via fallback algorithm [50321](https://github.com/WordPress/gutenberg/pull/50321) - -### Accessibility - -- BlockLockModal: restore focus on fallback toolbar button when original button is not rendered [51666](https://github.com/WordPress/gutenberg/pull/51666) -- Site Editor Sidebar: improvements to buttons [51762](https://github.com/WordPress/gutenberg/pull/51762) - -### Bug fixes - -#### Site Editor -- Disable the revision button if there is no clickable menu [51851](https://github.com/WordPress/gutenberg/pull/51851) -- Library: Fix misalignment of description in custom template parts [51868](https://github.com/WordPress/gutenberg/pull/51868) -- Library: Fix delete shortcut incorrectly bound to non-user patterns [51830](https://github.com/WordPress/gutenberg/pull/51830) -- Add distraction free to site editor -[51173](https://github.com/WordPress/gutenberg/pull/51173) -- Fix toolbar overlap in site editor -[51810](https://github.com/WordPress/gutenberg/pull/51810) -- Increase space between page meta and details section [51858](https://github.com/WordPress/gutenberg/pull/51858/) -- Fix missing MenuGroup segment in Site Editor header more menu [51860](https://github.com/WordPress/gutenberg/pull/51860) -- Update the add template modal design [51806](https://github.com/WordPress/gutenberg/pull/51806) -- Update text color of site editor menu item hover/active states [51847](https://github.com/WordPress/gutenberg/pull/51847) - -#### Block Library -- Buttons Block: add support for orientation-based block movers [51831](https://github.com/WordPress/gutenberg/pull/51831) - -#### Command Center -- Add manage all custom patterns command [51845](https://github.com/WordPress/gutenberg/pull/51845) -- Add another batch of commands to the site editor [51832](https://github.com/WordPress/gutenberg/pull/51832) -- Add UI commands to the post editor [51900](https://github.com/WordPress/gutenberg/pull/51900) - -#### Components -- Keep framer-motion from updating minor version [51894](https://github.com/WordPress/gutenberg/pull/51894) -- ConfirmDialog: Fix affirmative action being triggered an extra time when selecting a button via keyboard [51730](https://github.com/WordPress/gutenberg/pull/51730) -- Tweak more icons for high-resolution devices [51768](https://github.com/WordPress/gutenberg/pull/51768) -- ZStack: fix component bounding box to match children [51836](https://github.com/WordPress/gutenberg/pull/51836) - -#### Page Content Focus -- Switch to Page panel when deselecting a block [51881](https://github.com/WordPress/gutenberg/pull/51881) -- Don't show 'Back to page' notification when navigating away from page [51880](https://github.com/WordPress/gutenberg/pull/51880) -- useBlockSync(): Reset inner blocks when component unmounts [51783](https://github.com/WordPress/gutenberg/pull/51783) -- Fix black pixel appearing when block toolbar is empty [51779](https://github.com/WordPress/gutenberg/pull/51779) -- Only show Page Content Focus commands when in edit mode [51888](https://github.com/WordPress/gutenberg/pull/51888) - -## Contributors - -The following contributors merged PRs in this release: - -@jameskoster @priethor @richtabor @t-hamano @draganescu @noisysocks @tellthemachines @ciampo @ntsekouras @youknowriad @talldan @getdave @ajlende @mirka - - -= 16.1.0-rc.1 = += 16.1.0 = ## Changelog @@ -90,9 +19,12 @@ The following contributors merged PRs in this release: - Reusable blocks: Rename to 'Patterns' and add option to also add a non-synced Pattern. ([51144](https://github.com/WordPress/gutenberg/pull/51144)) - Site Editor: Add Library for Template Parts & Patterns Management. ([51078](https://github.com/WordPress/gutenberg/pull/51078)) -### Themes +#### Themes - Remove the experiment option for Block Theme Previews. ([50983](https://github.com/WordPress/gutenberg/pull/50983)) +#### Site Editor +- Add distraction free to site editor [51173](https://github.com/WordPress/gutenberg/pull/51173) + ### Enhancements #### Site Editor @@ -117,13 +49,16 @@ The following contributors merged PRs in this release: - Show actions for empty menus in Navigation on Browse mode. ([51420](https://github.com/WordPress/gutenberg/pull/51420)) - Unify welcome guides labels casing. ([51700](https://github.com/WordPress/gutenberg/pull/51700)) - Site editor sidebar: Add footer to template part and ensure nested template areas display. ([51669](https://github.com/WordPress/gutenberg/pull/51669)) +- Update colors in 'Site view'. ([51856](https://github.com/WordPress/gutenberg/pull/51856)) +- Site Editor: Make string to add Template parts and Patterns consistent and translatable [51743](https://github.com/WordPress/gutenberg/pull/51743) #### Components - ItemGroup: Update button focus styles to be more consistent. ([51576](https://github.com/WordPress/gutenberg/pull/51576)) - Toolbar: Use Ariakit instead of Reakit. ([51623](https://github.com/WordPress/gutenberg/pull/51623)) - Update: Adjust modal radius to be between frame and buttons. ([51254](https://github.com/WordPress/gutenberg/pull/51254)) -- `UnitControl`: Revamp support for changing unit by typing. ([39303](https://github.com/WordPress/gutenberg/pull/39303)) +- UnitControl: Revamp support for changing unit by typing. ([39303](https://github.com/WordPress/gutenberg/pull/39303)) - Move HeadinglevelDropdown to its own component. ([46003](https://github.com/WordPress/gutenberg/pull/46003)) +- Button: Introduce size prop [51842](https://github.com/WordPress/gutenberg/pull/51842) #### Block Editor - List View: Try showing blocks that are dragged (no longer hide them). ([51724](https://github.com/WordPress/gutenberg/pull/51724)) @@ -133,6 +68,7 @@ The following contributors merged PRs in this release: - Limit Copy/Paste Styles menu item. ([51503](https://github.com/WordPress/gutenberg/pull/51503)) - Force disable suggestions until URL field is dirty in Link Control. ([51354](https://github.com/WordPress/gutenberg/pull/51354)) - Top toolbar: Refine the icons on the right. ([51735](https://github.com/WordPress/gutenberg/pull/51735)) +- Improve LinkControl Edit UI. ([51712](https://github.com/WordPress/gutenberg/pull/51712)) #### Block Library - List block: Add numbering type selection. ([51186](https://github.com/WordPress/gutenberg/pull/51186)) @@ -147,12 +83,16 @@ The following contributors merged PRs in this release: - Post Author - don't show 0 in inspector controls. ([51345](https://github.com/WordPress/gutenberg/pull/51345)) - Remove anchor support from dynamic blocks. ([51288](https://github.com/WordPress/gutenberg/pull/51288)) - Site Tagline Block: Remove unnecessary square path from block icon SVG. ([51611](https://github.com/WordPress/gutenberg/pull/51611)) +- Add image block aspect ratio control [51545](https://github.com/WordPress/gutenberg/pull/51545) +- Updating social link attributes [51997](https://github.com/WordPress/gutenberg/pull/51997) #### Accessibility - Autocomplete: Announce results to screen readers when first becoming visible. ([51018](https://github.com/WordPress/gutenberg/pull/51018)) - List View: A11Y focus enhancements for edit-site based on modifications to edit-post. ([51404](https://github.com/WordPress/gutenberg/pull/51404)) - Add a description key to theme.json style variations. ([45242](https://github.com/WordPress/gutenberg/pull/45242)) - Focus first focusable on Link UI. ([51105](https://github.com/WordPress/gutenberg/pull/51105)) +- BlockLockModal: restore focus on fallback toolbar button when original button is not rendered [51666](https://github.com/WordPress/gutenberg/pull/51666) +- Site Editor Sidebar: improvements to buttons [51762](https://github.com/WordPress/gutenberg/pull/51762) #### Interactivity API - Add missing tests for image block. ([51305](https://github.com/WordPress/gutenberg/pull/51305)) @@ -197,15 +137,16 @@ The following contributors merged PRs in this release: - Add commands to access template, template parts and styles. ([51501](https://github.com/WordPress/gutenberg/pull/51501)) - Add global styles related commands. ([51637](https://github.com/WordPress/gutenberg/pull/51637)) -### Themes - +#### Themes - Block Theme Previews: Change the URL query string for more safety. ([51312](https://github.com/WordPress/gutenberg/pull/51312)) - Update the Save Button label when you're previewing a theme. ([51361](https://github.com/WordPress/gutenberg/pull/51361)) +#### Navigation +- Ensure there is always a Navigation available in the browse mode sidebar via fallback algorithm [50321](https://github.com/WordPress/gutenberg/pull/50321) + ### Bug Fixes #### Block Library - - Fix navigation error in library. ([51589](https://github.com/WordPress/gutenberg/pull/51589)) - Fix site editor rendering of Categories block. ([51329](https://github.com/WordPress/gutenberg/pull/51329)) - Gallery block - Add default value for innerBlockImages. ([51443](https://github.com/WordPress/gutenberg/pull/51443)) @@ -217,12 +158,17 @@ The following contributors merged PRs in this release: - Query Loop: Properly initialize and update `perPage` when we inherit from global query. ([51641](https://github.com/WordPress/gutenberg/pull/51641)) - Avatar block: Fix not 1:1 between the editor and the front-end. ([49963](https://github.com/WordPress/gutenberg/pull/49963)) - Spacer block: Fix invalid markup when set to fill. ([51317](https://github.com/WordPress/gutenberg/pull/51317)) +- Buttons Block: add support for orientation-based block movers [51831](https://github.com/WordPress/gutenberg/pull/51831) +- Rename navigation fallback classes from WP_ to Gutenberg_ [51959](https://github.com/WordPress/gutenberg/pull/51959) #### Commands - Show pages with any status in the command center. ([51324](https://github.com/WordPress/gutenberg/pull/51324)) +- Add manage all custom patterns command [51845](https://github.com/WordPress/gutenberg/pull/51845) +- Add another batch of commands to the site editor [51832](https://github.com/WordPress/gutenberg/pull/51832) +- Add UI commands to the post editor [51900](https://github.com/WordPress/gutenberg/pull/51900) +- Add preferences and keyboard shortcuts commands [51862](https://github.com/WordPress/gutenberg/pull/51862) #### Core Data - - Core Data: Fix ESLint warning for the 'useEntityRecord' hook. ([51562](https://github.com/WordPress/gutenberg/pull/51562)) - useEntityRecord: Fix destructure error when `enabled` option is `false`. ([51534](https://github.com/WordPress/gutenberg/pull/51534)) @@ -242,6 +188,16 @@ The following contributors merged PRs in this release: - Global styles: Fix back button tooltip. ([51725](https://github.com/WordPress/gutenberg/pull/51725)) - Site editor header: Fix document title back and shortcut color contrast. ([51442](https://github.com/WordPress/gutenberg/pull/51442)) - Site editor sidebar: Fix the heading hierarchy. ([51696](https://github.com/WordPress/gutenberg/pull/51696)) +- Disable the revision button if there is no clickable menu [51851](https://github.com/WordPress/gutenberg/pull/51851) +- Library: Fix misalignment of description in custom template parts [51868](https://github.com/WordPress/gutenberg/pull/51868) +- Library: Fix delete shortcut incorrectly bound to non-user patterns [51830](https://github.com/WordPress/gutenberg/pull/51830) +- Fix toolbar overlap in site editor [51810](https://github.com/WordPress/gutenberg/pull/51810) +- Increase space between page meta and details section [51858](https://github.com/WordPress/gutenberg/pull/51858/) +- Fix missing MenuGroup segment in Site Editor header more menu [51860](https://github.com/WordPress/gutenberg/pull/51860) +- Update the add template modal design [51806](https://github.com/WordPress/gutenberg/pull/51806) +- Update text color of site editor menu item hover/active states [51847](https://github.com/WordPress/gutenberg/pull/51847) +- Fix library command path [51837](https://github.com/WordPress/gutenberg/pull/51837) +- Restore sidebar in focus mode on Pattern click through in Browse Mode Library [51897](https://github.com/WordPress/gutenberg/pull/51897) #### Accessibility - Distraction Free: Avoid focus loss when enabling/disabling distraction free mode via the more menu. ([51627](https://github.com/WordPress/gutenberg/pull/51627)) @@ -257,6 +213,10 @@ The following contributors merged PRs in this release: - MediaPlaceholder: Fix position of URLPopover. ([51363](https://github.com/WordPress/gutenberg/pull/51363)) - Popover: Allow legitimate 0 positions to update popover position. ([51320](https://github.com/WordPress/gutenberg/pull/51320)) - Button: Remove unnecessary margin from dashicon. ([51395](https://github.com/WordPress/gutenberg/pull/51395)) +- Keep framer-motion from updating minor version [51894](https://github.com/WordPress/gutenberg/pull/51894) +- ConfirmDialog: Fix affirmative action being triggered an extra time when selecting a button via keyboard [51730](https://github.com/WordPress/gutenberg/pull/51730) +- Tweak more icons for high-resolution devices [51768](https://github.com/WordPress/gutenberg/pull/51768) +- ZStack: fix component bounding box to match children [51836](https://github.com/WordPress/gutenberg/pull/51836) #### Patterns - Library: Reinstate sidebar navigation menu editing for template parts. ([51825](https://github.com/WordPress/gutenberg/pull/51825)) @@ -279,6 +239,13 @@ The following contributors merged PRs in this release: #### Block Variations - [Block Library - Post Terms]: Custom taxonomies do not show icons when transforming from the toolbar. ([51476](https://github.com/WordPress/gutenberg/pull/51476)) +#### Page Content Focus +- Switch to Page panel when deselecting a block [51881](https://github.com/WordPress/gutenberg/pull/51881) +- Don't show 'Back to page' notification when navigating away from page [51880](https://github.com/WordPress/gutenberg/pull/51880) +- useBlockSync(): Reset inner blocks when component unmounts [51783](https://github.com/WordPress/gutenberg/pull/51783) +- Fix black pixel appearing when block toolbar is empty [51779](https://github.com/WordPress/gutenberg/pull/51779) +- Only show Page Content Focus commands when in edit mode [51888](https://github.com/WordPress/gutenberg/pull/51888) + ### Performance #### Site Editor @@ -309,7 +276,8 @@ The following contributors merged PRs in this release: - Updated getEntityRecord doc by using selector instead of dispatch. ([51298](https://github.com/WordPress/gutenberg/pull/51298)) ### Code Quality -- Add tests for WP_Classic_To_Block_Menu_Converter class. ([51410](https://github.com/WordPress/gutenberg/pull/51410)) + +#### General - Adopt lock-unlock.js and private-apis.js convention. ([51322](https://github.com/WordPress/gutenberg/pull/51322)) - Formats: Avoid rerendering language edit component when typing. ([51440](https://github.com/WordPress/gutenberg/pull/51440)) - Keyboard Shortcut: Clean up shortcut names. ([51739](https://github.com/WordPress/gutenberg/pull/51739)) @@ -320,6 +288,7 @@ The following contributors merged PRs in this release: - Require relocated class files for back-compat in WordPress releases. ([51670](https://github.com/WordPress/gutenberg/pull/51670)) #### Block Library +- Add tests for WP_Classic_To_Block_Menu_Converter class. ([51410](https://github.com/WordPress/gutenberg/pull/51410)) - Details: Set 'clientId' as useSelect dependency. ([51634](https://github.com/WordPress/gutenberg/pull/51634)) - Move Navigation fallback files to 6.3 directory. ([51572](https://github.com/WordPress/gutenberg/pull/51572)) - Navigation: Fix ListView deprecation notice. ([51094](https://github.com/WordPress/gutenberg/pull/51094)) @@ -406,7 +375,6 @@ The following contributors merged PRs in this release: - Performance tests: Make theme versions consistent cross-env. ([50905](https://github.com/WordPress/gutenberg/pull/50905)) - Performance tests: Update base point to compare. ([51381](https://github.com/WordPress/gutenberg/pull/51381)) - #### Build Tooling - Babel config: Enable useSpread option for JSX transform to reduce transpilation. ([51574](https://github.com/WordPress/gutenberg/pull/51574)) - Lodash: Remove from lint staged type check. ([51698](https://github.com/WordPress/gutenberg/pull/51698)) @@ -430,7 +398,9 @@ The following PRs were merged by first time contributors: The following contributors merged PRs in this release: -@aaronrobertshaw @afercia @alexstine @andrewserong @aristath @artemiomorales @aurooba @bangank36 @c4rl0sbr4v0 @carolinan @ciampo @dcalhoun @derekblank @diegohaz @draganescu @ellatrix @fabiankaegy @fluiddot @geriux @getdave @glendaviesnz @jameskoster @jasmussen @jeryj @jhnstn @jsnajdr @juanfra @kozer @luisherranz @MaggieCabrera @Mamaduka @matiasbenedetto @mcliwanow @mcsf @mikachan @n2erjo00 @noahtallen @noisysocks @ntsekouras @oandregal @okmttdhr @paulopmt1 @pbking @peterwilsoncc @pooja-muchandikar @ramonjd @richtabor @samnajian @SantosGuillamot @SavPhill @SaxonF @scruffian @shimotmk @Sidsector9 @SiobhyB @spacedmonkey @stokesman @sunyatasattva @t-hamano @talldan @tellthemachines @tyxla @walbo @WunderBart @xerpa43 @youknowriad +@aaronrobertshaw @afercia @alexstine @andrewserong @aristath @artemiomorales @aurooba @bangank36 @c4rl0sbr4v0 @carolinan @ciampo @dcalhoun @derekblank @diegohaz @draganescu @ellatrix @fabiankaegy @fluiddot @geriux @getdave @glendaviesnz @jameskoster @jasmussen @jeryj @jhnstn @jsnajdr @juanfra @kozer @luisherranz @MaggieCabrera @Mamaduka @matiasbenedetto @mcliwanow @mcsf @mikachan @n2erjo00 @noahtallen @noisysocks @ntsekouras @oandregal @okmttdhr @paulopmt1 @pbking @peterwilsoncc @pooja-muchandikar @ramonjd @richtabor @samnajian @SantosGuillamot @SavPhill @SaxonF @scruffian @shimotmk @Sidsector9 @SiobhyB @spacedmonkey @stokesman @sunyatasattva @t-hamano @talldan @tellthemachines @tyxla @walbo @WunderBart @xerpa43 @youknowriad @priethor @ajlende @mirka + + = 16.0.0 = From 866727e9f31bd98bde01df61a92676f6adb02744 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Wed, 28 Jun 2023 17:55:31 +1000 Subject: [PATCH 045/266] Patterns: Fix setting of sync status for fully synced patterns (#51952) --- .../edit-site/src/components/create-pattern-modal/index.js | 5 ++++- .../edit-site/src/components/page-library/use-patterns.js | 2 +- packages/editor/src/components/post-sync-status/index.js | 7 ++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/edit-site/src/components/create-pattern-modal/index.js b/packages/edit-site/src/components/create-pattern-modal/index.js index 2cef6b21ebbbad..5180ad5d870684 100644 --- a/packages/edit-site/src/components/create-pattern-modal/index.js +++ b/packages/edit-site/src/components/create-pattern-modal/index.js @@ -54,7 +54,10 @@ export default function CreatePatternModal( { title: name || __( 'Untitled Pattern' ), content: '', status: 'publish', - meta: { sync_status: syncType }, + meta: + syncType === SYNC_TYPES.unsynced + ? { sync_status: syncType } + : undefined, }, { throwOnError: true } ); diff --git a/packages/edit-site/src/components/page-library/use-patterns.js b/packages/edit-site/src/components/page-library/use-patterns.js index 9034dc43421fc0..6f3b7407e917dd 100644 --- a/packages/edit-site/src/components/page-library/use-patterns.js +++ b/packages/edit-site/src/components/page-library/use-patterns.js @@ -145,7 +145,7 @@ const reusableBlockToPattern = ( reusableBlock ) => ( { categories: reusableBlock.wp_pattern, id: reusableBlock.id, name: reusableBlock.slug, - syncStatus: reusableBlock.meta?.sync_status, + syncStatus: reusableBlock.meta?.sync_status || SYNC_TYPES.full, title: reusableBlock.title.raw, type: reusableBlock.type, reusableBlock, diff --git a/packages/editor/src/components/post-sync-status/index.js b/packages/editor/src/components/post-sync-status/index.js index 40476111a82c17..392d33563ef78f 100644 --- a/packages/editor/src/components/post-sync-status/index.js +++ b/packages/editor/src/components/post-sync-status/index.js @@ -26,13 +26,10 @@ export default function PostSyncStatus() { editPost( { meta: { ...meta, - wp_block: - syncStatus === 'unsynced' - ? { sync_status: syncStatus } - : null, + sync_status: syncStatus === 'unsynced' ? syncStatus : null, }, } ); - const syncStatus = meta?.wp_block?.sync_status; + const syncStatus = meta?.sync_status; const isFullySynced = ! syncStatus; return ( From bfb1dcb08c4c9e94ff7a7513162d1127e4517276 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Wed, 28 Jun 2023 17:57:09 +1000 Subject: [PATCH 046/266] Library: Reinstate manage all template parts page (#51961) --- .../src/components/page-main/index.js | 5 +- .../components/page-template-parts/index.js | 105 ++++++++++++++++++ .../index.js | 31 +++--- .../index.js | 16 ++- .../index.js | 2 + .../edit-site/src/components/sidebar/index.js | 6 +- .../edit-site/src/utils/get-is-list-page.js | 1 + 7 files changed, 146 insertions(+), 20 deletions(-) create mode 100644 packages/edit-site/src/components/page-template-parts/index.js diff --git a/packages/edit-site/src/components/page-main/index.js b/packages/edit-site/src/components/page-main/index.js index ebcf35dfb0c1f5..a6c05886888c45 100644 --- a/packages/edit-site/src/components/page-main/index.js +++ b/packages/edit-site/src/components/page-main/index.js @@ -6,8 +6,9 @@ import { privateApis as routerPrivateApis } from '@wordpress/router'; /** * Internal dependencies */ -import PageTemplates from '../page-templates'; import PageLibrary from '../page-library'; +import PageTemplateParts from '../page-template-parts'; +import PageTemplates from '../page-templates'; import { unlock } from '../../lock-unlock'; const { useLocation } = unlock( routerPrivateApis ); @@ -19,6 +20,8 @@ export default function PageMain() { if ( path === '/wp_template/all' ) { return ; + } else if ( path === '/wp_template_part/all' ) { + return ; } else if ( path === '/library' ) { return ; } diff --git a/packages/edit-site/src/components/page-template-parts/index.js b/packages/edit-site/src/components/page-template-parts/index.js new file mode 100644 index 00000000000000..0a50f839279793 --- /dev/null +++ b/packages/edit-site/src/components/page-template-parts/index.js @@ -0,0 +1,105 @@ +/** + * WordPress dependencies + */ +import { + VisuallyHidden, + __experimentalHeading as Heading, + __experimentalVStack as VStack, +} from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; +import { useSelect } from '@wordpress/data'; +import { store as coreStore, useEntityRecords } from '@wordpress/core-data'; +import { decodeEntities } from '@wordpress/html-entities'; + +/** + * Internal dependencies + */ +import Page from '../page'; +import Table from '../table'; +import Link from '../routes/link'; +import AddedBy from '../list/added-by'; +import TemplateActions from '../template-actions'; +import AddNewTemplate from '../add-new-template'; +import { store as editSiteStore } from '../../store'; + +export default function PageTemplateParts() { + const { records: templateParts } = useEntityRecords( + 'postType', + 'wp_template_part', + { + per_page: -1, + } + ); + + const { canCreate } = useSelect( ( select ) => { + const { supportsTemplatePartsMode } = + select( editSiteStore ).getSettings(); + return { + postType: select( coreStore ).getPostType( 'wp_template_part' ), + canCreate: ! supportsTemplatePartsMode, + }; + } ); + + const columns = [ + { + header: __( 'Template Part' ), + cell: ( templatePart ) => ( + + + + { decodeEntities( + templatePart.title?.rendered || + templatePart.slug + ) } + + + + ), + maxWidth: 400, + }, + { + header: __( 'Added by' ), + cell: ( templatePart ) => ( + + ), + }, + { + header: { __( 'Actions' ) }, + cell: ( templatePart ) => ( + + ), + }, + ]; + + return ( + + ) + } + > + { templateParts && ( + + ) } + + ); +} diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-library/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-library/index.js index d3cfcbece6be0e..270e0ed59afed0 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-library/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-library/index.js @@ -21,6 +21,7 @@ import SidebarNavigationScreen from '../sidebar-navigation-screen'; import CategoryItem from './category-item'; import { DEFAULT_CATEGORY, DEFAULT_TYPE } from '../page-library/utils'; import { store as editSiteStore } from '../../store'; +import { useLink } from '../routes/link'; import usePatternCategories from './use-pattern-categories'; import useTemplatePartAreas from './use-template-part-areas'; @@ -46,6 +47,22 @@ export default function SidebarNavigationScreenLibrary() { return !! settings.supportsTemplatePartsMode; }, [] ); + const templatePartsLink = useLink( { path: '/wp_template_part/all' } ); + const footer = ! isMobileViewport ? ( + + + { __( 'Manage all template parts' ) } + + + { __( 'Manage all custom patterns' ) } + + + ) : undefined; + return ( } - footer={ - - { ! isMobileViewport && ( - - { __( 'Manage all custom patterns' ) } - - ) } - - } + footer={ footer } content={ <> { isLoading && __( 'Loading library' ) } 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 279b5a0de298d8..182a570d8a95d9 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 @@ -1,10 +1,11 @@ /** * WordPress dependencies */ -import { __ } from '@wordpress/i18n'; +import { __experimentalUseNavigator as useNavigator } from '@wordpress/components'; import { useDispatch } from '@wordpress/data'; +import { __ } from '@wordpress/i18n'; import { pencil } from '@wordpress/icons'; -import { __experimentalUseNavigator as useNavigator } from '@wordpress/components'; +import { getQueryArgs } from '@wordpress/url'; /** * Internal dependencies @@ -19,6 +20,7 @@ import { unlock } from '../../lock-unlock'; export default function SidebarNavigationScreenPattern() { const { params } = useNavigator(); + const { categoryType } = getQueryArgs( window.location.href ); const { postType, postId } = params; const { setCanvasMode } = unlock( useDispatch( editSiteStore ) ); @@ -27,6 +29,14 @@ export default function SidebarNavigationScreenPattern() { const patternDetails = usePatternDetails( postType, postId ); const content = useNavigationMenuContent( postType, postId ); + // The absence of a category type in the query params for template parts + // indicates the user has arrived at the template part via the "manage all" + // page and the back button should return them to that list page. + const backPath = + ! categoryType && postType === 'wp_template_part' + ? '/wp_template_part/all' + : '/library'; + return ( } - backPath={ '/library' } + backPath={ backPath } content={ content } { ...patternDetails } /> diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-templates-browse/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-templates-browse/index.js index 67e979c26e2cea..b5a0e4d195885d 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-templates-browse/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-templates-browse/index.js @@ -21,6 +21,7 @@ const config = { description: __( 'Create new template parts, or reset any customizations made to the template parts supplied by your theme.' ), + backPath: '/library', }, }; @@ -32,6 +33,7 @@ export default function SidebarNavigationScreenTemplatesBrowse() { ); } diff --git a/packages/edit-site/src/components/sidebar/index.js b/packages/edit-site/src/components/sidebar/index.js index eef37a44339956..df6a5665031406 100644 --- a/packages/edit-site/src/components/sidebar/index.js +++ b/packages/edit-site/src/components/sidebar/index.js @@ -59,12 +59,12 @@ function SidebarScreens() { + + + - - - diff --git a/packages/edit-site/src/utils/get-is-list-page.js b/packages/edit-site/src/utils/get-is-list-page.js index 963287308ac9a3..11827432071bd3 100644 --- a/packages/edit-site/src/utils/get-is-list-page.js +++ b/packages/edit-site/src/utils/get-is-list-page.js @@ -15,6 +15,7 @@ export default function getIsListPage( ) { return ( path === '/wp_template/all' || + path === '/wp_template_part/all' || ( path === '/library' && // Don't treat "/library" without categoryType and categoryId as a list page // in mobile because the sidebar covers the whole page. From 553d0f68ea8453fcc3c1ff3ebf2fc0921dc31156 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Wed, 28 Jun 2023 18:01:42 +1000 Subject: [PATCH 047/266] Patterns: Fix sidebar tab label (#51953) --- .../sidebar-edit-mode/settings-header/index.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/edit-site/src/components/sidebar-edit-mode/settings-header/index.js b/packages/edit-site/src/components/sidebar-edit-mode/settings-header/index.js index 10ce08e8e7401b..7bc951524e7e5c 100644 --- a/packages/edit-site/src/components/sidebar-edit-mode/settings-header/index.js +++ b/packages/edit-site/src/components/sidebar-edit-mode/settings-header/index.js @@ -18,6 +18,12 @@ import { STORE_NAME } from '../../../store/constants'; import { SIDEBAR_BLOCK, SIDEBAR_TEMPLATE } from '../constants'; import { store as editSiteStore } from '../../../store'; +const entityLabels = { + wp_navigation: __( 'Navigation' ), + wp_block: __( 'Pattern' ), + wp_template: __( 'Template' ), +}; + const SettingsHeader = ( { sidebarName } ) => { const { hasPageContentFocus, entityType } = useSelect( ( select ) => { const { getEditedPostType, hasPageContentFocus: _hasPageContentFocus } = @@ -29,8 +35,7 @@ const SettingsHeader = ( { sidebarName } ) => { }; } ); - const entityLabel = - entityType === 'wp_navigation' ? __( 'Navigation' ) : __( 'Template' ); + const entityLabel = entityLabels[ entityType ] || entityLabels.wp_template; const { enableComplementaryArea } = useDispatch( interfaceStore ); const openTemplateSettings = () => From 42cd04a296f044517f700861767a4e0f79561a45 Mon Sep 17 00:00:00 2001 From: Marin Atanasov <8436925+tyxla@users.noreply.github.com> Date: Wed, 28 Jun 2023 11:24:03 +0300 Subject: [PATCH 048/266] Lodash: Refactor away from _.kebabCase() in add page modal (#51911) --- packages/edit-site/src/components/add-new-page/index.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/edit-site/src/components/add-new-page/index.js b/packages/edit-site/src/components/add-new-page/index.js index 5b3f6426251117..0c8efa3dfaa0ba 100644 --- a/packages/edit-site/src/components/add-new-page/index.js +++ b/packages/edit-site/src/components/add-new-page/index.js @@ -1,8 +1,3 @@ -/** - * External dependencies - */ -import { kebabCase } from 'lodash'; - /** * WordPress dependencies */ @@ -41,7 +36,7 @@ export default function AddNewPageModal( { onSave, onClose } ) { { status: 'draft', title, - slug: kebabCase( title || __( 'No title' ) ), + slug: title || __( 'No title' ), }, { throwOnError: true } ); From d8e8c28bafeebd67c586982edae8de6c9f632021 Mon Sep 17 00:00:00 2001 From: Carolina Nymark Date: Wed, 28 Jun 2023 10:25:11 +0200 Subject: [PATCH 049/266] Add link color theme support (#51775) --- docs/how-to-guides/themes/theme-json.md | 2 +- docs/how-to-guides/themes/theme-support.md | 8 ++++++++ lib/class-wp-theme-json-resolver-gutenberg.php | 12 ++++++++++++ packages/core-data/src/entity-types/theme.ts | 4 ++++ 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/docs/how-to-guides/themes/theme-json.md b/docs/how-to-guides/themes/theme-json.md index 4a3dc8f70a85e6..2212f98a1c870c 100644 --- a/docs/how-to-guides/themes/theme-json.md +++ b/docs/how-to-guides/themes/theme-json.md @@ -342,8 +342,8 @@ To retain backward compatibility, the existing `add_theme_support` declarations | `editor-color-palette` | Provide the list of colors via `color.palette`. | | `editor-font-sizes` | Provide the list of font size via `typography.fontSizes`. | | `editor-gradient-presets` | Provide the list of gradients via `color.gradients`. | -| `experimental-link-color` | Set `color.link` to `true`. `experimental-link-color` will be removed when the plugin requires WordPress 5.9 as the minimum version. | | `appearance-tools` | Set `appearanceTools` to `true`. | +| `link-color ` | Set `color.link` to `true`. | #### Presets diff --git a/docs/how-to-guides/themes/theme-support.md b/docs/how-to-guides/themes/theme-support.md index 423c8c6a4e2815..45402691795cdc 100644 --- a/docs/how-to-guides/themes/theme-support.md +++ b/docs/how-to-guides/themes/theme-support.md @@ -477,6 +477,14 @@ Use this setting to enable the following Global Styles settings: add_theme_support( 'appearance-tools' ); ``` +## Link color + +Use this to enable the link color setting: + +```php +add_theme_support( 'link-color' ); +``` + ## Block Based Template Parts Block Based Template parts allow administrators to edit parts of the site using blocks. This is off by default, and requires the theme to opt in by declaring support: diff --git a/lib/class-wp-theme-json-resolver-gutenberg.php b/lib/class-wp-theme-json-resolver-gutenberg.php index 33fd4defd51917..73cae004bfbad2 100644 --- a/lib/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/class-wp-theme-json-resolver-gutenberg.php @@ -319,6 +319,18 @@ public static function get_theme_data( $deprecated = array(), $options = array() // Classic themes without a theme.json don't support global duotone. $theme_support_data['settings']['color']['defaultDuotone'] = false; + // Allow themes to enable link colors via theme_support. + if ( current_theme_supports( 'link-color' ) ) { + $theme_support_data['settings']['color']['link'] = true; + } + if ( current_theme_supports( 'experimental-link-color' ) ) { + _doing_it_wrong( + current_theme_supports( 'experimental-link-color' ), + __( '`experimental-link-color` is no longer supported. Use `link-color` instead.', 'gutenberg' ), + '6.3.0' + ); + } + // BEGIN EXPERIMENTAL. // Allow themes to enable appearance tools via theme_support. // This feature was backported for WordPress 6.2 as of https://core.trac.wordpress.org/ticket/56487 diff --git a/packages/core-data/src/entity-types/theme.ts b/packages/core-data/src/entity-types/theme.ts index 04904ae2501f00..6e15738dd992e0 100644 --- a/packages/core-data/src/entity-types/theme.ts +++ b/packages/core-data/src/entity-types/theme.ts @@ -141,6 +141,10 @@ declare module './base-entity-records' { * Post formats supported. */ formats: PostFormat[]; + /** + * Whether link colors are enabled. + */ + 'link-color': boolean; /** * The post types that support thumbnails or true if all post types are supported. */ From edab5925ad5f949674702c0a288d88e98466bb4b Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Wed, 28 Jun 2023 18:37:27 +1000 Subject: [PATCH 050/266] Library: Add sync status to pattern details screen (#51954) --- .../index.js | 3 -- .../use-pattern-details.js | 44 ++++++++++++++++++- 2 files changed, 43 insertions(+), 4 deletions(-) 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 182a570d8a95d9..349cc5d43b431d 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 @@ -14,7 +14,6 @@ import SidebarButton from '../sidebar-button'; import SidebarNavigationScreen from '../sidebar-navigation-screen'; import useInitEditedEntityFromURL from '../sync-state-with-url/use-init-edited-entity-from-url'; import usePatternDetails from './use-pattern-details'; -import useNavigationMenuContent from './use-navigation-menu-content'; import { store as editSiteStore } from '../../store'; import { unlock } from '../../lock-unlock'; @@ -27,7 +26,6 @@ export default function SidebarNavigationScreenPattern() { useInitEditedEntityFromURL(); const patternDetails = usePatternDetails( postType, postId ); - const content = useNavigationMenuContent( postType, postId ); // The absence of a category type in the query params for template parts // indicates the user has arrived at the template part via the "manage all" @@ -47,7 +45,6 @@ export default function SidebarNavigationScreenPattern() { /> } backPath={ backPath } - content={ content } { ...patternDetails } /> ); diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-pattern/use-pattern-details.js b/packages/edit-site/src/components/sidebar-navigation-screen-pattern/use-pattern-details.js index 028b1c781f8f2c..dfc367ea0b97dc 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-pattern/use-pattern-details.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-pattern/use-pattern-details.js @@ -11,7 +11,14 @@ import { Icon } from '@wordpress/components'; */ import { useAddedBy } from '../list/added-by'; import useEditedEntityRecord from '../use-edited-entity-record'; +import useNavigationMenuContent from './use-navigation-menu-content'; import SidebarNavigationScreenDetailsFooter from '../sidebar-navigation-screen-details-footer'; +import { + SidebarNavigationScreenDetailsPanel, + SidebarNavigationScreenDetailsPanelRow, + SidebarNavigationScreenDetailsPanelLabel, + SidebarNavigationScreenDetailsPanelValue, +} from '../sidebar-navigation-screen-details-panel'; export default function usePatternDetails( postType, postId ) { const { getDescription, getTitle, record } = useEditedEntityRecord( @@ -82,5 +89,40 @@ export default function usePatternDetails( postType, postId ) { /> ) : null; - return { title, description, footer }; + const details = []; + + if ( postType === 'wp_block' ) { + details.push( { + label: __( 'Syncing' ), + value: + record.meta?.sync_status === 'unsynced' + ? __( 'Not synced' ) + : __( 'Fully synced' ), + } ); + } + + const content = ( + <> + { !! details.length && ( + + { details.map( ( { label, value } ) => ( + + + { label } + + + { value } + + + ) ) } + + ) } + { useNavigationMenuContent( postType, postId ) } + + ); + + return { title, description, content, footer }; } From 16486bd946f918d581e4818b73ceaaed82349f71 Mon Sep 17 00:00:00 2001 From: Carolina Nymark Date: Wed, 28 Jun 2023 10:37:47 +0200 Subject: [PATCH 051/266] Only allow the term description block in the site editor. (#51053) --- docs/reference-guides/core-blocks.md | 1 + packages/block-library/src/term-description/block.json | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index 73e4186fce9b67..0306e026b5f45a 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -877,6 +877,7 @@ Edit the different global regions of your site, like the header, footer, sidebar Display the description of categories, tags and custom taxonomies when viewing an archive. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/term-description)) - **Name:** core/term-description +- **Experimental:** fse - **Category:** theme - **Supports:** align (full, wide), color (background, link, text), spacing (margin, padding), typography (fontSize, lineHeight), ~~html~~ - **Attributes:** textAlign diff --git a/packages/block-library/src/term-description/block.json b/packages/block-library/src/term-description/block.json index fc91a4aff4c484..beee12eb674b03 100644 --- a/packages/block-library/src/term-description/block.json +++ b/packages/block-library/src/term-description/block.json @@ -1,6 +1,7 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", "apiVersion": 3, + "__experimental": "fse", "name": "core/term-description", "title": "Term Description", "category": "theme", From 4ef676e38062889dbd391931284e542e1cfc9fad Mon Sep 17 00:00:00 2001 From: Carolina Nymark Date: Wed, 28 Jun 2023 10:58:37 +0200 Subject: [PATCH 052/266] Add border theme_support (#51777) --- docs/how-to-guides/themes/theme-json.md | 1 + docs/how-to-guides/themes/theme-support.md | 8 ++++++++ lib/class-wp-theme-json-resolver-gutenberg.php | 8 ++++++++ packages/core-data/src/entity-types/theme.ts | 4 ++++ 4 files changed, 21 insertions(+) diff --git a/docs/how-to-guides/themes/theme-json.md b/docs/how-to-guides/themes/theme-json.md index 2212f98a1c870c..697592adf3236c 100644 --- a/docs/how-to-guides/themes/theme-json.md +++ b/docs/how-to-guides/themes/theme-json.md @@ -343,6 +343,7 @@ To retain backward compatibility, the existing `add_theme_support` declarations | `editor-font-sizes` | Provide the list of font size via `typography.fontSizes`. | | `editor-gradient-presets` | Provide the list of gradients via `color.gradients`. | | `appearance-tools` | Set `appearanceTools` to `true`. | +| `border` | Set `border: color, radius, style, width` to `true`. | | `link-color ` | Set `color.link` to `true`. | #### Presets diff --git a/docs/how-to-guides/themes/theme-support.md b/docs/how-to-guides/themes/theme-support.md index 45402691795cdc..eae72f255fac2c 100644 --- a/docs/how-to-guides/themes/theme-support.md +++ b/docs/how-to-guides/themes/theme-support.md @@ -477,6 +477,14 @@ Use this setting to enable the following Global Styles settings: add_theme_support( 'appearance-tools' ); ``` +## Border + +Use this to enable all border settings: + +```php +add_theme_support( 'border' ); +``` + ## Link color Use this to enable the link color setting: diff --git a/lib/class-wp-theme-json-resolver-gutenberg.php b/lib/class-wp-theme-json-resolver-gutenberg.php index 73cae004bfbad2..6e9d05cd7f238b 100644 --- a/lib/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/class-wp-theme-json-resolver-gutenberg.php @@ -319,6 +319,14 @@ public static function get_theme_data( $deprecated = array(), $options = array() // Classic themes without a theme.json don't support global duotone. $theme_support_data['settings']['color']['defaultDuotone'] = false; + // Allow themes to enable all border settings via theme_support. + if ( current_theme_supports( 'border' ) ) { + $theme_support_data['settings']['border']['color'] = true; + $theme_support_data['settings']['border']['radius'] = true; + $theme_support_data['settings']['border']['style'] = true; + $theme_support_data['settings']['border']['width'] = true; + } + // Allow themes to enable link colors via theme_support. if ( current_theme_supports( 'link-color' ) ) { $theme_support_data['settings']['color']['link'] = true; diff --git a/packages/core-data/src/entity-types/theme.ts b/packages/core-data/src/entity-types/theme.ts index 6e15738dd992e0..761c6461d4aca8 100644 --- a/packages/core-data/src/entity-types/theme.ts +++ b/packages/core-data/src/entity-types/theme.ts @@ -85,6 +85,10 @@ declare module './base-entity-records' { * Whether posts and comments RSS feed links are added to head. */ 'automatic-feed-links': boolean; + /** + * Whether border settings are enabled. + */ + border: boolean; /** * Custom background if defined by the theme. */ From a09f124c4b90a3cbe5b193f796fa29e4d9b8b4fa Mon Sep 17 00:00:00 2001 From: Dave Smith Date: Wed, 28 Jun 2023 10:50:48 +0100 Subject: [PATCH 053/266] Make the entire preview clickable in order to enter "edit" mode in focus mode (#51973) --- .../edit-site/src/components/block-editor/editor-canvas.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 fad43f04106400..34c23cc699bc28 100644 --- a/packages/edit-site/src/components/block-editor/editor-canvas.js +++ b/packages/edit-site/src/components/block-editor/editor-canvas.js @@ -87,7 +87,9 @@ function EditorCanvas( { enableResizing, settings, children, ...props } ) { // which isn't a requirement in auto resize mode. enableResizing ? 'min-height:0!important;' : '' }}body{position:relative; ${ - canvasMode === 'view' ? 'cursor: pointer;' : '' + canvasMode === 'view' + ? 'cursor: pointer; height: 100vh' + : '' }}}` } { children } From 56ebbcbf7922a214663ba9ef116a8de779670b5b Mon Sep 17 00:00:00 2001 From: Carolina Nymark Date: Wed, 28 Jun 2023 11:53:20 +0200 Subject: [PATCH 054/266] Add Typography: text orientation (writing mode) (#50822) * Add Typography: text orientation (writing mode) * fix spacing CS issues * try to fix white space issue * Rename text orientation to writing mode * Add new text orientation icons --- .../theme-json-reference/theme-json-living.md | 2 + lib/block-supports/typography.php | 10 ++- lib/class-wp-theme-json-gutenberg.php | 6 +- lib/theme.json | 3 +- .../src/components/global-styles/hooks.js | 2 + .../global-styles/typography-panel.js | 40 +++++++++++ packages/block-editor/src/components/index.js | 1 + .../components/writing-mode-control/index.js | 68 +++++++++++++++++++ .../writing-mode-control/style.scss | 18 +++++ packages/block-editor/src/hooks/supports.js | 7 ++ packages/block-editor/src/hooks/typography.js | 2 + packages/block-editor/src/hooks/utils.js | 3 + .../block-library/src/paragraph/block.json | 1 + .../src/post-navigation-link/block.json | 1 + packages/blocks/src/api/constants.js | 5 ++ .../components/global-styles/stories/index.js | 1 + packages/icons/src/index.js | 2 + packages/icons/src/library/text-horizontal.js | 12 ++++ packages/icons/src/library/text-vertical.js | 12 ++++ .../style-engine/class-wp-style-engine.php | 6 ++ .../src/styles/typography/index.ts | 13 ++++ packages/style-engine/src/types.ts | 1 + schemas/json/theme.json | 16 +++++ 23 files changed, 229 insertions(+), 3 deletions(-) create mode 100644 packages/block-editor/src/components/writing-mode-control/index.js create mode 100644 packages/block-editor/src/components/writing-mode-control/style.scss create mode 100644 packages/icons/src/library/text-horizontal.js create mode 100644 packages/icons/src/library/text-vertical.js diff --git a/docs/reference-guides/theme-json-reference/theme-json-living.md b/docs/reference-guides/theme-json-reference/theme-json-living.md index 32997395754307..a2e88320cf58bb 100644 --- a/docs/reference-guides/theme-json-reference/theme-json-living.md +++ b/docs/reference-guides/theme-json-reference/theme-json-living.md @@ -158,6 +158,7 @@ Settings related to typography. | lineHeight | boolean | false | | | textColumns | boolean | false | | | textDecoration | boolean | true | | +| writingMode | boolean | false | | | textTransform | boolean | true | | | dropCap | boolean | true | | | fontSizes | array | | fluid, name, size, slug | @@ -239,6 +240,7 @@ Typography styles. | lineHeight | string, object | | | textColumns | string | | | textDecoration | string, object | | +| writingMode | string, object | | | textTransform | string, object | | --- diff --git a/lib/block-supports/typography.php b/lib/block-supports/typography.php index 469b429f9b3ddf..047231a6eed105 100644 --- a/lib/block-supports/typography.php +++ b/lib/block-supports/typography.php @@ -29,6 +29,7 @@ function gutenberg_register_typography_support( $block_type ) { $has_text_columns_support = _wp_array_get( $typography_supports, array( 'textColumns' ), false ); $has_text_decoration_support = _wp_array_get( $typography_supports, array( '__experimentalTextDecoration' ), false ); $has_text_transform_support = _wp_array_get( $typography_supports, array( '__experimentalTextTransform' ), false ); + $has_writing_mode_support = _wp_array_get( $typography_supports, array( '__experimentalWritingMode' ), false ); $has_typography_support = $has_font_family_support || $has_font_size_support @@ -38,7 +39,8 @@ function gutenberg_register_typography_support( $block_type ) { || $has_line_height_support || $has_text_columns_support || $has_text_decoration_support - || $has_text_transform_support; + || $has_text_transform_support + || $has_writing_mode_support; if ( ! $block_type->attributes ) { $block_type->attributes = array(); @@ -96,6 +98,7 @@ function gutenberg_apply_typography_support( $block_type, $block_attributes ) { $has_text_columns_support = _wp_array_get( $typography_supports, array( 'textColumns' ), false ); $has_text_decoration_support = _wp_array_get( $typography_supports, array( '__experimentalTextDecoration' ), false ); $has_text_transform_support = _wp_array_get( $typography_supports, array( '__experimentalTextTransform' ), false ); + $has_writing_mode_support = _wp_array_get( $typography_supports, array( '__experimentalWritingMode' ), false ); // Whether to skip individual block support features. $should_skip_font_size = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'fontSize' ); @@ -107,6 +110,7 @@ function gutenberg_apply_typography_support( $block_type, $block_attributes ) { $should_skip_text_decoration = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'textDecoration' ); $should_skip_text_transform = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'textTransform' ); $should_skip_letter_spacing = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'letterSpacing' ); + $should_skip_writing_mode = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'writingMode' ); $typography_block_styles = array(); if ( $has_font_size_support && ! $should_skip_font_size ) { @@ -158,6 +162,10 @@ function gutenberg_apply_typography_support( $block_type, $block_attributes ) { gutenberg_typography_get_preset_inline_style_value( $block_attributes['style']['typography']['letterSpacing'], 'letter-spacing' ); } + if ( $has_writing_mode_support && ! $should_skip_writing_mode && isset( $block_attributes['style']['typography']['writingMode'] ) ) { + $typography_block_styles['writingMode'] = _wp_array_get( $block_attributes, array( 'style', 'typography', 'writingMode' ), null ); + } + $attributes = array(); $styles = gutenberg_style_engine_get_styles( array( 'typography' => $typography_block_styles ), diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 7313afeba91bca..0745ee06b84a23 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -202,6 +202,7 @@ class WP_Theme_JSON_Gutenberg { * `--wp--style--root--padding-*`, and `box-shadow` properties, * removed the `--wp--style--block-gap` property. * @since 6.2.0 Added `outline-*`, and `min-height` properties. + * @since 6.3.0 Added `writing-mode` property. * * @var array */ @@ -260,6 +261,7 @@ class WP_Theme_JSON_Gutenberg { 'text-transform' => array( 'typography', 'textTransform' ), 'filter' => array( 'filter', 'duotone' ), 'box-shadow' => array( 'shadow' ), + 'writing-mode' => array( 'typography', 'writingMode' ), ); /** @@ -339,7 +341,7 @@ class WP_Theme_JSON_Gutenberg { * @since 6.1.0 Added `layout.definitions` and `useRootPaddingAwareAlignments`. * @since 6.2.0 Added `dimensions.minHeight`, 'shadow.presets', 'shadow.defaultPresets', * `position.fixed` and `position.sticky`. - * @since 6.3.0 Removed `layout.definitions`. + * @since 6.3.0 Removed `layout.definitions`. Added `typography.writingMode`. * @var array */ const VALID_SETTINGS = array( @@ -406,6 +408,7 @@ class WP_Theme_JSON_Gutenberg { 'textColumns' => null, 'textDecoration' => null, 'textTransform' => null, + 'writingMode' => null, ), 'behaviors' => null, ); @@ -468,6 +471,7 @@ class WP_Theme_JSON_Gutenberg { 'textColumns' => null, 'textDecoration' => null, 'textTransform' => null, + 'writingMode' => null, ), 'css' => null, ); diff --git a/lib/theme.json b/lib/theme.json index 2786c281822d69..5c64526b1a2534 100644 --- a/lib/theme.json +++ b/lib/theme.json @@ -274,7 +274,8 @@ "lineHeight": false, "textColumns": false, "textDecoration": true, - "textTransform": true + "textTransform": true, + "writingMode": false }, "blocks": { "core/button": { diff --git a/packages/block-editor/src/components/global-styles/hooks.js b/packages/block-editor/src/components/global-styles/hooks.js index d5ad1375031698..2efdc187f8f6bb 100644 --- a/packages/block-editor/src/components/global-styles/hooks.js +++ b/packages/block-editor/src/components/global-styles/hooks.js @@ -71,6 +71,7 @@ const VALID_SETTINGS = [ 'typography.textColumns', 'typography.textDecoration', 'typography.textTransform', + 'typography.writingMode', ]; export const useGlobalStylesReset = () => { @@ -292,6 +293,7 @@ export function useSettingsForBlockElement( 'letterSpacing', 'textTransform', 'textDecoration', + 'writingMode', ].forEach( ( key ) => { if ( ! supportedStyles.includes( key ) ) { updatedSettings.typography = { diff --git a/packages/block-editor/src/components/global-styles/typography-panel.js b/packages/block-editor/src/components/global-styles/typography-panel.js index 37ed048172e883..371855ab0ae23e 100644 --- a/packages/block-editor/src/components/global-styles/typography-panel.js +++ b/packages/block-editor/src/components/global-styles/typography-panel.js @@ -19,6 +19,7 @@ import LineHeightControl from '../line-height-control'; import LetterSpacingControl from '../letter-spacing-control'; import TextTransformControl from '../text-transform-control'; import TextDecorationControl from '../text-decoration-control'; +import WritingModeControl from '../writing-mode-control'; import { getValueFromVariable } from './utils'; import { setImmutably } from '../../utils/object'; @@ -32,6 +33,7 @@ export function useHasTypographyPanel( settings ) { const hasLetterSpacing = useHasLetterSpacingControl( settings ); const hasTextTransform = useHasTextTransformControl( settings ); const hasTextDecoration = useHasTextDecorationControl( settings ); + const hasWritingMode = useHasWritingModeControl( settings ); const hasTextColumns = useHasTextColumnsControl( settings ); const hasFontSize = useHasFontSizeControl( settings ); @@ -43,6 +45,7 @@ export function useHasTypographyPanel( settings ) { hasTextTransform || hasFontSize || hasTextDecoration || + hasWritingMode || hasTextColumns ); } @@ -103,6 +106,10 @@ function useHasTextDecorationControl( settings ) { return settings?.typography?.textDecoration; } +function useHasWritingModeControl( settings ) { + return settings?.typography?.writingMode; +} + function useHasTextColumnsControl( settings ) { return settings?.typography?.textColumns; } @@ -138,6 +145,7 @@ const DEFAULT_CONTROLS = { letterSpacing: true, textTransform: true, textDecoration: true, + writingMode: true, textColumns: true, }; @@ -310,6 +318,21 @@ export default function TypographyPanel( { const hasTextDecoration = () => !! value?.typography?.textDecoration; const resetTextDecoration = () => setTextDecoration( undefined ); + // Text Orientation + const hasWritingModeControl = useHasWritingModeControl( settings ); + const writingMode = decodeValue( inheritedValue?.typography?.writingMode ); + const setWritingMode = ( newValue ) => { + onChange( + setImmutably( + value, + [ 'typography', 'writingMode' ], + newValue || undefined + ) + ); + }; + const hasWritingMode = () => !! value?.typography?.writingMode; + const resetWritingMode = () => setWritingMode( undefined ); + const resetAllFilter = useCallback( ( previousValue ) => { return { ...previousValue, @@ -456,6 +479,23 @@ export default function TypographyPanel( { /> ) } + { hasWritingModeControl && ( + + + + ) } { hasTextTransformControl && ( + + { __( 'Orientation' ) } + +
+ { WRITING_MODES.map( ( writingMode ) => { + return ( +
+ + ); +} diff --git a/packages/block-editor/src/components/writing-mode-control/style.scss b/packages/block-editor/src/components/writing-mode-control/style.scss new file mode 100644 index 00000000000000..4b865dc0282c08 --- /dev/null +++ b/packages/block-editor/src/components/writing-mode-control/style.scss @@ -0,0 +1,18 @@ +.block-editor-writing-mode-control { + border: 0; + margin: 0; + padding: 0; + + .block-editor-writing-mode-control__buttons { + // 4px of padding makes the row 40px high, same as an input. + padding: $grid-unit-05 0; + display: flex; + } + + .components-button.has-icon { + height: $grid-unit-40; + margin-right: $grid-unit-05; + min-width: $grid-unit-40; + padding: 0; + } +} diff --git a/packages/block-editor/src/hooks/supports.js b/packages/block-editor/src/hooks/supports.js index ead8ca89aa47d6..2cf08d46fa8fe2 100644 --- a/packages/block-editor/src/hooks/supports.js +++ b/packages/block-editor/src/hooks/supports.js @@ -30,11 +30,17 @@ const TEXT_COLUMNS_SUPPORT_KEY = 'typography.textColumns'; * decorations e.g. settings found in `block.json`. */ const TEXT_DECORATION_SUPPORT_KEY = 'typography.__experimentalTextDecoration'; +/** + * Key within block settings' supports array indicating support for writing mode + * e.g. settings found in `block.json`. + */ +const WRITING_MODE_SUPPORT_KEY = 'typography.__experimentalWritingMode'; /** * Key within block settings' supports array indicating support for text * transforms e.g. settings found in `block.json`. */ const TEXT_TRANSFORM_SUPPORT_KEY = 'typography.__experimentalTextTransform'; + /** * Key within block settings' supports array indicating support for letter-spacing * e.g. settings found in `block.json`. @@ -50,6 +56,7 @@ const TYPOGRAPHY_SUPPORT_KEYS = [ TEXT_COLUMNS_SUPPORT_KEY, TEXT_DECORATION_SUPPORT_KEY, TEXT_TRANSFORM_SUPPORT_KEY, + WRITING_MODE_SUPPORT_KEY, LETTER_SPACING_SUPPORT_KEY, ]; const SPACING_SUPPORT_KEY = 'spacing'; diff --git a/packages/block-editor/src/hooks/typography.js b/packages/block-editor/src/hooks/typography.js index cb98c4098c4774..c7d1a6ba3b1443 100644 --- a/packages/block-editor/src/hooks/typography.js +++ b/packages/block-editor/src/hooks/typography.js @@ -30,6 +30,7 @@ const TEXT_DECORATION_SUPPORT_KEY = 'typography.__experimentalTextDecoration'; const TEXT_COLUMNS_SUPPORT_KEY = 'typography.textColumns'; const FONT_STYLE_SUPPORT_KEY = 'typography.__experimentalFontStyle'; const FONT_WEIGHT_SUPPORT_KEY = 'typography.__experimentalFontWeight'; +const WRITING_MODE_SUPPORT_KEY = 'typography.__experimentalWritingMode'; export const TYPOGRAPHY_SUPPORT_KEY = 'typography'; export const TYPOGRAPHY_SUPPORT_KEYS = [ LINE_HEIGHT_SUPPORT_KEY, @@ -39,6 +40,7 @@ export const TYPOGRAPHY_SUPPORT_KEYS = [ FONT_FAMILY_SUPPORT_KEY, TEXT_COLUMNS_SUPPORT_KEY, TEXT_DECORATION_SUPPORT_KEY, + WRITING_MODE_SUPPORT_KEY, TEXT_TRANSFORM_SUPPORT_KEY, LETTER_SPACING_SUPPORT_KEY, ]; diff --git a/packages/block-editor/src/hooks/utils.js b/packages/block-editor/src/hooks/utils.js index 8c525e78e4c6e7..ccc5dcd9ff2e2a 100644 --- a/packages/block-editor/src/hooks/utils.js +++ b/packages/block-editor/src/hooks/utils.js @@ -136,6 +136,7 @@ export function useBlockSettings( name, parentLayout ) { const lineHeight = useSetting( 'typography.lineHeight' ); const textColumns = useSetting( 'typography.textColumns' ); const textDecoration = useSetting( 'typography.textDecoration' ); + const writingMode = useSetting( 'typography.writingMode' ); const textTransform = useSetting( 'typography.textTransform' ); const letterSpacing = useSetting( 'typography.letterSpacing' ); const padding = useSetting( 'spacing.padding' ); @@ -211,6 +212,7 @@ export function useBlockSettings( name, parentLayout ) { textDecoration, textTransform, letterSpacing, + writingMode, }, spacing: { spacingSizes: { @@ -244,6 +246,7 @@ export function useBlockSettings( name, parentLayout ) { textDecoration, textTransform, letterSpacing, + writingMode, padding, margin, blockGap, diff --git a/packages/block-library/src/paragraph/block.json b/packages/block-library/src/paragraph/block.json index cbabc108eca311..7e13b13dc4feb9 100644 --- a/packages/block-library/src/paragraph/block.json +++ b/packages/block-library/src/paragraph/block.json @@ -58,6 +58,7 @@ "__experimentalFontWeight": true, "__experimentalLetterSpacing": true, "__experimentalTextTransform": true, + "__experimentalWritingMode": true, "__experimentalDefaultControls": { "fontSize": true } diff --git a/packages/block-library/src/post-navigation-link/block.json b/packages/block-library/src/post-navigation-link/block.json index 4e7df560c69b33..e1b6d4fa90a40c 100644 --- a/packages/block-library/src/post-navigation-link/block.json +++ b/packages/block-library/src/post-navigation-link/block.json @@ -45,6 +45,7 @@ "__experimentalTextTransform": true, "__experimentalTextDecoration": true, "__experimentalLetterSpacing": true, + "__experimentalWritingMode": true, "__experimentalDefaultControls": { "fontSize": true } diff --git a/packages/blocks/src/api/constants.js b/packages/blocks/src/api/constants.js index 6fe04c07de1bfb..fb4e4efa4e083c 100644 --- a/packages/blocks/src/api/constants.js +++ b/packages/blocks/src/api/constants.js @@ -215,6 +215,11 @@ export const __EXPERIMENTAL_STYLE_PROPERTY = { support: [ 'typography', '__experimentalLetterSpacing' ], useEngine: true, }, + writingMode: { + value: [ 'typography', 'writingMode' ], + support: [ 'typography', '__experimentalWritingMode' ], + useEngine: true, + }, '--wp--style--root--padding': { value: [ 'spacing', 'padding' ], support: [ 'spacing', 'padding' ], diff --git a/packages/edit-site/src/components/global-styles/stories/index.js b/packages/edit-site/src/components/global-styles/stories/index.js index f5e31cc142c0f7..f04387295c458a 100644 --- a/packages/edit-site/src/components/global-styles/stories/index.js +++ b/packages/edit-site/src/components/global-styles/stories/index.js @@ -367,6 +367,7 @@ const BASE_SETTINGS = { textColumns: false, textDecoration: true, textTransform: true, + writingMode: false, fluid: true, fontFamilies: { theme: [ diff --git a/packages/icons/src/index.js b/packages/icons/src/index.js index 903d6f9455a873..1f15620ff6b22f 100644 --- a/packages/icons/src/index.js +++ b/packages/icons/src/index.js @@ -255,6 +255,8 @@ export { default as sidesRight } from './library/sides-right'; export { default as sidesTop } from './library/sides-top'; export { default as sidesVertical } from './library/sides-vertical'; export { default as textColor } from './library/text-color'; +export { default as textHorizontal } from './library/text-horizontal'; +export { default as textVertical } from './library/text-vertical'; export { default as tablet } from './library/tablet'; export { default as title } from './library/title'; export { default as tip } from './library/tip'; diff --git a/packages/icons/src/library/text-horizontal.js b/packages/icons/src/library/text-horizontal.js new file mode 100644 index 00000000000000..3f82ee1fba4cbd --- /dev/null +++ b/packages/icons/src/library/text-horizontal.js @@ -0,0 +1,12 @@ +/** + * WordPress dependencies + */ +import { SVG, Path } from '@wordpress/primitives'; + +const textHorizontal = ( + + + +); + +export default textHorizontal; diff --git a/packages/icons/src/library/text-vertical.js b/packages/icons/src/library/text-vertical.js new file mode 100644 index 00000000000000..406561bee97cc8 --- /dev/null +++ b/packages/icons/src/library/text-vertical.js @@ -0,0 +1,12 @@ +/** + * WordPress dependencies + */ +import { SVG, Path } from '@wordpress/primitives'; + +const textVertical = ( + + + +); + +export default textVertical; diff --git a/packages/style-engine/class-wp-style-engine.php b/packages/style-engine/class-wp-style-engine.php index d62f245364103e..ec66168bc1a9a6 100644 --- a/packages/style-engine/class-wp-style-engine.php +++ b/packages/style-engine/class-wp-style-engine.php @@ -241,6 +241,12 @@ final class WP_Style_Engine { ), 'path' => array( 'typography', 'letterSpacing' ), ), + 'writingMode' => array( + 'property_keys' => array( + 'default' => 'writing-mode', + ), + 'path' => array( 'typography', 'writingMode' ), + ), ), ); diff --git a/packages/style-engine/src/styles/typography/index.ts b/packages/style-engine/src/styles/typography/index.ts index 66e89d0e562090..92c40d2e156198 100644 --- a/packages/style-engine/src/styles/typography/index.ts +++ b/packages/style-engine/src/styles/typography/index.ts @@ -112,6 +112,18 @@ const textTransform = { }, }; +const writingMode = { + name: 'writingMode', + generate: ( style: Style, options: StyleOptions ) => { + return generateRule( + style, + options, + [ 'typography', 'writingMode' ], + 'writingMode' + ); + }, +}; + export default [ fontFamily, fontSize, @@ -122,4 +134,5 @@ export default [ textColumns, textDecoration, textTransform, + writingMode, ]; diff --git a/packages/style-engine/src/types.ts b/packages/style-engine/src/types.ts index 23d3e38cc43c22..4b6a9aa72257ed 100644 --- a/packages/style-engine/src/types.ts +++ b/packages/style-engine/src/types.ts @@ -55,6 +55,7 @@ export interface Style { textColumns?: CSSProperties[ 'columnCount' ]; textDecoration?: CSSProperties[ 'textDecoration' ]; textTransform?: CSSProperties[ 'textTransform' ]; + writingMode?: CSSProperties[ 'writingMode' ]; }; color?: { text?: CSSProperties[ 'color' ]; diff --git a/schemas/json/theme.json b/schemas/json/theme.json index 03a966f7f3eeea..7f0e712283b603 100644 --- a/schemas/json/theme.json +++ b/schemas/json/theme.json @@ -458,6 +458,11 @@ "type": "boolean", "default": true }, + "writingMode": { + "description": "Allow users to set the writing mode.", + "type": "boolean", + "default": false + }, "textTransform": { "description": "Allow users to set custom text transforms.", "type": "boolean", @@ -1508,6 +1513,17 @@ } ] }, + "writingMode": { + "description": "Sets the `writing-mode` CSS property.", + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "#/definitions/refComplete" + } + ] + }, "textTransform": { "description": "Sets the `text-transform` CSS property.", "oneOf": [ From 0d2ad6314af916a96bf25ad2cffbfe34b2915a89 Mon Sep 17 00:00:00 2001 From: Kai Hao Date: Wed, 28 Jun 2023 17:57:49 +0800 Subject: [PATCH 055/266] Fix missing snackbars in Library (#52021) --- packages/edit-site/src/components/page/index.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/edit-site/src/components/page/index.js b/packages/edit-site/src/components/page/index.js index d2f2a4f9c08811..a713989a3ce0a2 100644 --- a/packages/edit-site/src/components/page/index.js +++ b/packages/edit-site/src/components/page/index.js @@ -7,6 +7,7 @@ import classnames from 'classnames'; * WordPress dependencies */ import { NavigableRegion } from '@wordpress/interface'; +import { EditorSnackbars } from '@wordpress/editor'; /** * Internal dependencies @@ -32,7 +33,10 @@ export default function Page( { actions={ actions } /> ) } -
{ children }
+
+ { children } + +
); } From 874f13d4f7ec611abe64275887fb07b6fcd02c58 Mon Sep 17 00:00:00 2001 From: Saxon Fletcher Date: Wed, 28 Jun 2023 20:04:42 +1000 Subject: [PATCH 056/266] Update pattern creation modal in library (#51946) --- .../src/components/create-pattern-modal/index.js | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/packages/edit-site/src/components/create-pattern-modal/index.js b/packages/edit-site/src/components/create-pattern-modal/index.js index 5180ad5d870684..2c7eb4599dee1a 100644 --- a/packages/edit-site/src/components/create-pattern-modal/index.js +++ b/packages/edit-site/src/components/create-pattern-modal/index.js @@ -26,7 +26,7 @@ export default function CreatePatternModal( { onError, } ) { const [ name, setName ] = useState( '' ); - const [ syncType, setSyncType ] = useState( SYNC_TYPES.full ); + const [ syncType, setSyncType ] = useState( SYNC_TYPES.unsynced ); const [ isSubmitting, setIsSubmitting ] = useState( false ); const onSyncChange = () => { @@ -80,8 +80,6 @@ export default function CreatePatternModal( { onRequestClose={ closeModal } overlayClassName="edit-site-create-pattern-modal" > -

{ __( 'Turn this block into a pattern to reuse later' ) }

-
{ event.preventDefault(); @@ -103,13 +101,11 @@ export default function CreatePatternModal( { __nextHasNoMarginBottom /> From cce8801dad9af395c8710cf7a7e8629c995ee9ce Mon Sep 17 00:00:00 2001 From: James Koster Date: Wed, 28 Jun 2023 11:23:12 +0100 Subject: [PATCH 057/266] !important (#52025) --- .../src/components/sidebar-navigation-screen/style.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/edit-site/src/components/sidebar-navigation-screen/style.scss b/packages/edit-site/src/components/sidebar-navigation-screen/style.scss index 7fe1cfc5925d63..1fecf49d712215 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen/style.scss +++ b/packages/edit-site/src/components/sidebar-navigation-screen/style.scss @@ -110,7 +110,7 @@ background: $gray-800; .components-button { - color: $gray-200; + color: $gray-200 !important; } } .components-input-control__input { From 90b3a509a1da3eaed3129ca8e7a41888fd71739f Mon Sep 17 00:00:00 2001 From: Joen A <1204802+jasmussen@users.noreply.github.com> Date: Wed, 28 Jun 2023 12:46:26 +0200 Subject: [PATCH 058/266] Editor initrial appender: Zero out margins in constrained layouts. (#52026) --- .../components/default-block-appender/content.scss | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/block-editor/src/components/default-block-appender/content.scss b/packages/block-editor/src/components/default-block-appender/content.scss index 9eecf9a7e007aa..48aac077096c2a 100644 --- a/packages/block-editor/src/components/default-block-appender/content.scss +++ b/packages/block-editor/src/components/default-block-appender/content.scss @@ -26,6 +26,17 @@ opacity: 0.62; } + // In "constrained" layout containers, the first and last paragraphs have their margins zeroed out. + // In the case of this appender, it needs to apply those same rules to avoid layout shifts. + // Such shifts happen when the bottom margin of the Title block has been set to less than the default 1em margin of paragraphs. + :where(body .is-layout-constrained) & { + > :first-child:first-child { + margin-block-start: 0; + } + + // Since this particular appender will only ever appear on an entirely empty document, we don't account for last-child. + } + // Dropzone. .components-drop-zone__content-icon { display: none; From 2c604ed6aa0ce3dfa47e0dd78a3a42c9f5e56ceb Mon Sep 17 00:00:00 2001 From: Saxon Fletcher Date: Wed, 28 Jun 2023 20:58:34 +1000 Subject: [PATCH 059/266] Update custom patterns label to 'My patterns' (#51949) * rename custom patterns to my patterns * Add my patterns label to inserter and show at the top --------- Co-authored-by: Daniel Richards --- .../components/inserter/block-patterns-tab.js | 1 + .../inserter/hooks/use-patterns-state.js | 2 +- .../inserter/reusable-blocks-tab.js | 2 +- .../src/admin-navigation-commands.js | 2 +- .../src/components/page-library/utils.js | 6 ++--- .../index.js | 23 +++++++++++++++++-- .../use-my-patterns.js | 23 +++++++++++++++++++ .../use-pattern-categories.js | 19 +-------------- 8 files changed, 52 insertions(+), 26 deletions(-) create mode 100644 packages/edit-site/src/components/sidebar-navigation-screen-library/use-my-patterns.js diff --git a/packages/block-editor/src/components/inserter/block-patterns-tab.js b/packages/block-editor/src/components/inserter/block-patterns-tab.js index 5c2720fd0502e3..f66d27ac06170d 100644 --- a/packages/block-editor/src/components/inserter/block-patterns-tab.js +++ b/packages/block-editor/src/components/inserter/block-patterns-tab.js @@ -33,6 +33,7 @@ const noop = () => {}; // Preferred order of pattern categories. Any other categories should // be at the bottom without any re-ordering. const patternCategoriesOrder = [ + 'custom', 'featured', 'posts', 'text', diff --git a/packages/block-editor/src/components/inserter/hooks/use-patterns-state.js b/packages/block-editor/src/components/inserter/hooks/use-patterns-state.js index 2a99e637ed1237..805a89ca8ff0c4 100644 --- a/packages/block-editor/src/components/inserter/hooks/use-patterns-state.js +++ b/packages/block-editor/src/components/inserter/hooks/use-patterns-state.js @@ -14,7 +14,7 @@ import { store as blockEditorStore } from '../../../store'; const CUSTOM_CATEGORY = { name: 'custom', - label: __( 'Custom patterns' ), + label: __( 'My patterns' ), description: __( 'Custom patterns add by site users' ), }; diff --git a/packages/block-editor/src/components/inserter/reusable-blocks-tab.js b/packages/block-editor/src/components/inserter/reusable-blocks-tab.js index 65930fa9fcd4a6..c16d5f1a78e543 100644 --- a/packages/block-editor/src/components/inserter/reusable-blocks-tab.js +++ b/packages/block-editor/src/components/inserter/reusable-blocks-tab.js @@ -67,7 +67,7 @@ export function ReusableBlocksTab( { rootClientId, onInsert, onHover } ) { post_type: 'wp_block', } ) } > - { __( 'Manage custom patterns' ) } + { __( 'Manage my patterns' ) } diff --git a/packages/core-commands/src/admin-navigation-commands.js b/packages/core-commands/src/admin-navigation-commands.js index 577e7258df0b63..a72b0c7dd5ab9b 100644 --- a/packages/core-commands/src/admin-navigation-commands.js +++ b/packages/core-commands/src/admin-navigation-commands.js @@ -24,7 +24,7 @@ export function useAdminNavigationCommands() { } ); useCommand( { name: 'core/manage-reusable-blocks', - label: __( 'Manage all custom patterns' ), + label: __( 'Manage all of my patterns' ), callback: () => { document.location.href = 'edit.php?post_type=wp_block'; }, diff --git a/packages/edit-site/src/components/page-library/utils.js b/packages/edit-site/src/components/page-library/utils.js index a9f1d7a658483e..bbdff872fe355a 100644 --- a/packages/edit-site/src/components/page-library/utils.js +++ b/packages/edit-site/src/components/page-library/utils.js @@ -1,9 +1,9 @@ -export const DEFAULT_CATEGORY = 'header'; -export const DEFAULT_TYPE = 'wp_template_part'; +export const DEFAULT_CATEGORY = 'my-patterns'; +export const DEFAULT_TYPE = 'wp_block'; export const PATTERNS = 'pattern'; export const TEMPLATE_PARTS = 'wp_template_part'; export const USER_PATTERNS = 'wp_block'; -export const USER_PATTERN_CATEGORY = 'custom-patterns'; +export const USER_PATTERN_CATEGORY = 'my-patterns'; export const CORE_PATTERN_SOURCES = [ 'core', diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-library/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-library/index.js index 270e0ed59afed0..5aa32e7587ebd2 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-library/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-library/index.js @@ -10,7 +10,7 @@ import { useSelect } from '@wordpress/data'; import { getTemplatePartIcon } from '@wordpress/editor'; import { __ } from '@wordpress/i18n'; import { getQueryArgs } from '@wordpress/url'; -import { file } from '@wordpress/icons'; +import { file, starFilled } from '@wordpress/icons'; /** * Internal dependencies @@ -23,6 +23,7 @@ import { DEFAULT_CATEGORY, DEFAULT_TYPE } from '../page-library/utils'; import { store as editSiteStore } from '../../store'; import { useLink } from '../routes/link'; import usePatternCategories from './use-pattern-categories'; +import useMyPatterns from './use-my-patterns'; import useTemplatePartAreas from './use-template-part-areas'; const templatePartAreaLabels = { @@ -41,6 +42,7 @@ export default function SidebarNavigationScreenLibrary() { const { templatePartAreas, hasTemplateParts, isLoading } = useTemplatePartAreas(); const { patternCategories, hasPatterns } = usePatternCategories(); + const { myPatterns, hasPatterns: hasMyPatterns } = useMyPatterns(); const isTemplatePartsMode = useSelect( ( select ) => { const settings = select( editSiteStore ).getSettings(); @@ -58,7 +60,7 @@ export default function SidebarNavigationScreenLibrary() { href="edit.php?post_type=wp_block" withChevron > - { __( 'Manage all custom patterns' ) } + { __( 'Manage all of my patterns' ) } ) : undefined; @@ -86,6 +88,23 @@ export default function SidebarNavigationScreenLibrary() { ) } + { hasMyPatterns && ( + + + + ) } { hasTemplateParts && ( { Object.entries( templatePartAreas ).map( diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-library/use-my-patterns.js b/packages/edit-site/src/components/sidebar-navigation-screen-library/use-my-patterns.js new file mode 100644 index 00000000000000..e3d5cc297164a2 --- /dev/null +++ b/packages/edit-site/src/components/sidebar-navigation-screen-library/use-my-patterns.js @@ -0,0 +1,23 @@ +/** + * WordPress dependencies + */ +import { store as coreStore } from '@wordpress/core-data'; +import { useSelect } from '@wordpress/data'; +import { __ } from '@wordpress/i18n'; + +export default function useMyPatterns() { + const myPatterns = useSelect( ( select ) => + select( coreStore ).getEntityRecords( 'postType', 'wp_block', { + per_page: -1, + } ) + ); + + return { + myPatterns: { + count: myPatterns?.length || 0, + name: 'my-patterns', + label: __( 'My patterns' ), + }, + hasPatterns: !! myPatterns?.length, + }; +} diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-library/use-pattern-categories.js b/packages/edit-site/src/components/sidebar-navigation-screen-library/use-pattern-categories.js index a787f8c04c6390..96491018d07722 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-library/use-pattern-categories.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-library/use-pattern-categories.js @@ -1,10 +1,7 @@ /** * WordPress dependencies */ -import { store as coreStore } from '@wordpress/core-data'; -import { useSelect } from '@wordpress/data'; import { useMemo } from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; /** * Internal dependencies @@ -15,11 +12,6 @@ import useThemePatterns from './use-theme-patterns'; export default function usePatternCategories() { const defaultCategories = useDefaultPatternCategories(); const themePatterns = useThemePatterns(); - const userPatterns = useSelect( ( select ) => - select( coreStore ).getEntityRecords( 'postType', 'wp_block', { - per_page: -1, - } ) - ); const patternCategories = useMemo( () => { const categoryMap = {}; @@ -48,17 +40,8 @@ export default function usePatternCategories() { } } ); - // Add "Your Patterns" category for user patterns if there are any. - if ( userPatterns?.length ) { - categoriesWithCounts.push( { - count: userPatterns.length || 0, - name: 'custom-patterns', - label: __( 'Custom patterns' ), - } ); - } - return categoriesWithCounts; - }, [ defaultCategories, themePatterns, userPatterns ] ); + }, [ defaultCategories, themePatterns ] ); return { patternCategories, hasPatterns: !! patternCategories.length }; } From ea769180f5541104ab5e6522a056dd973e6a30ef Mon Sep 17 00:00:00 2001 From: Rich Tabor Date: Wed, 28 Jun 2023 07:01:42 -0400 Subject: [PATCH 060/266] Patterns: Use "detached" copy consistently (#51993) --- .../block-actions-menu.native.js | 12 ++++-------- packages/block-library/src/block/edit.js | 4 ++-- packages/block-library/src/block/edit.native.js | 16 ++++++---------- .../specs/editor/various/reusable-blocks.test.js | 6 +++--- .../reusable-blocks-manage-button.js | 4 ++-- 5 files changed, 17 insertions(+), 25 deletions(-) diff --git a/packages/block-editor/src/components/block-mobile-toolbar/block-actions-menu.native.js b/packages/block-editor/src/components/block-mobile-toolbar/block-actions-menu.native.js index e896d6054a8ff0..48be5cb8a9c00b 100644 --- a/packages/block-editor/src/components/block-mobile-toolbar/block-actions-menu.native.js +++ b/packages/block-editor/src/components/block-mobile-toolbar/block-actions-menu.native.js @@ -213,16 +213,12 @@ const BlockActionsMenu = ( { id: 'convertToRegularBlocksOption', label: innerBlockCount > 1 - ? __( 'Convert to regular blocks' ) - : __( 'Convert to regular block' ), + ? __( 'Detach patterns' ) + : __( 'Detach pattern' ), value: 'convertToRegularBlocksOption', onSelect: () => { - const successNotice = - innerBlockCount > 1 - ? /* translators: %s: name of the reusable block */ - __( '%s converted to regular blocks' ) - : /* translators: %s: name of the reusable block */ - __( '%s converted to regular block' ); + /* translators: %s: name of the synced block */ + const successNotice = __( '%s detached' ); createSuccessNotice( sprintf( successNotice, diff --git a/packages/block-library/src/block/edit.js b/packages/block-library/src/block/edit.js index cc1ec16aeedc24..0b1f3b01b75b9d 100644 --- a/packages/block-library/src/block/edit.js +++ b/packages/block-library/src/block/edit.js @@ -119,8 +119,8 @@ export default function ReusableBlockEdit( { attributes: { ref }, clientId } ) { onClick={ () => convertBlockToStatic( clientId ) } label={ innerBlockCount > 1 - ? __( 'Convert to regular blocks' ) - : __( 'Convert to regular block' ) + ? __( 'Detach patterns' ) + : __( 'Detach pattern' ) } icon={ ungroup } showTooltip diff --git a/packages/block-library/src/block/edit.native.js b/packages/block-library/src/block/edit.native.js index e72aee3858eaf2..3466baa6bad713 100644 --- a/packages/block-library/src/block/edit.native.js +++ b/packages/block-library/src/block/edit.native.js @@ -132,12 +132,8 @@ export default function ReusableBlockEdit( { } const onConvertToRegularBlocks = useCallback( () => { - const successNotice = - innerBlockCount > 1 - ? /* translators: %s: name of the reusable block */ - __( '%s converted to regular blocks' ) - : /* translators: %s: name of the reusable block */ - __( '%s converted to regular block' ); + /* translators: %s: name of the synced block */ + const successNotice = __( '%s detached' ); createSuccessNotice( sprintf( successNotice, title ) ); clearSelectedBlock(); @@ -182,17 +178,17 @@ export default function ReusableBlockEdit( { { innerBlockCount > 1 ? __( - 'Alternatively, you can detach and edit these blocks separately by tapping “Convert to regular blocks”.' + 'Alternatively, you can detach and edit these blocks separately by tapping “Detach patterns”.' ) : __( - 'Alternatively, you can detach and edit this block separately by tapping “Convert to regular block”.' + 'Alternatively, you can detach and edit this block separately by tapping “Detach pattern”.' ) } 1 - ? __( 'Convert to regular blocks' ) - : __( 'Convert to regular block' ) + ? __( 'Detach patterns' ) + : __( 'Detach pattern' ) } separatorType="topFullWidth" onPress={ onConvertToRegularBlocks } diff --git a/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js b/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js index 6d9eaa562b48f5..1ffd4e24143362 100644 --- a/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js +++ b/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js @@ -113,7 +113,7 @@ describe( 'Reusable blocks', () => { await insertReusableBlock( 'Surprised greeting block' ); // Convert block to a regular block. - await clickBlockToolbarButton( 'Convert to regular block' ); + await clickBlockToolbarButton( 'Detach pattern' ); // Check that we have a paragraph block on the page. const paragraphBlock = await canvas().$( @@ -221,7 +221,7 @@ describe( 'Reusable blocks', () => { await insertReusableBlock( 'Multi-selection reusable block' ); // Convert block to a regular block. - await clickBlockToolbarButton( 'Convert to regular blocks' ); + await clickBlockToolbarButton( 'Detach patterns' ); // Check that we have two paragraph blocks on the page. expect( await getEditedPostContent() ).toMatchSnapshot(); @@ -353,7 +353,7 @@ describe( 'Reusable blocks', () => { // Convert back to regular blocks. await clickBlockToolbarButton( 'Select Pattern' ); - await clickBlockToolbarButton( 'Convert to regular block' ); + await clickBlockToolbarButton( 'Detach pattern' ); await page.waitForXPath( selector, { hidden: true, } ); 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 faffefe605ed96..6f339058885111 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 @@ -58,8 +58,8 @@ function ReusableBlocksManageButton( { clientId } ) { { canRemove && ( convertBlockToStatic( clientId ) }> { innerBlockCount > 1 - ? __( 'Convert to regular blocks' ) - : __( 'Convert to regular block' ) } + ? __( 'Detach patterns' ) + : __( 'Detach pattern' ) } ) } From d488747461e5226dd4b04a81cb0cccd8be830fee Mon Sep 17 00:00:00 2001 From: Dave Smith Date: Wed, 28 Jun 2023 12:53:16 +0100 Subject: [PATCH 061/266] Removes unused call (#51988) --- .../sidebar-navigation-screen-navigation-menu/index.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) 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 6bce0f356f7400..92810d1411fc39 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 @@ -33,11 +33,8 @@ export default function SidebarNavigationScreenNavigationMenu() { const { isSaving, isDeleting } = useSelect( ( select ) => { - const { - isSavingEntityRecord, - isDeletingEntityRecord, - getEditedEntityRecord: getEditedEntityRecordSelector, - } = select( coreStore ); + const { isSavingEntityRecord, isDeletingEntityRecord } = + select( coreStore ); return { isSaving: isSavingEntityRecord( 'postType', postType, postId ), @@ -46,7 +43,6 @@ export default function SidebarNavigationScreenNavigationMenu() { postType, postId ), - getEditedEntityRecord: getEditedEntityRecordSelector, }; }, [ postId ] From 2ecd216a93a30b18185a0383844440462ab32cb2 Mon Sep 17 00:00:00 2001 From: Carolina Nymark Date: Wed, 28 Jun 2023 13:56:57 +0200 Subject: [PATCH 062/266] Add template part icons to the library grid items (#51963) --- .../src/components/page-library/grid-item.js | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/packages/edit-site/src/components/page-library/grid-item.js b/packages/edit-site/src/components/page-library/grid-item.js index 586602d1ac05d3..fa6793ca781786 100644 --- a/packages/edit-site/src/components/page-library/grid-item.js +++ b/packages/edit-site/src/components/page-library/grid-item.js @@ -20,7 +20,13 @@ import { useInstanceId } from '@wordpress/compose'; import { useDispatch } from '@wordpress/data'; import { useState } from '@wordpress/element'; import { __, sprintf } from '@wordpress/i18n'; -import { Icon, moreHorizontal } from '@wordpress/icons'; +import { + Icon, + header, + footer, + symbolFilled, + moreHorizontal, +} from '@wordpress/icons'; import { store as noticesStore } from '@wordpress/notices'; import { store as reusableBlocksStore } from '@wordpress/reusable-blocks'; import { DELETE, BACKSPACE } from '@wordpress/keycodes'; @@ -89,6 +95,15 @@ export default function GridItem( { categoryId, composite, icon, item } ) { ariaDescription = item.description; } + let itemIcon = icon; + if ( categoryId === 'header' ) { + itemIcon = header; + } else if ( categoryId === 'footer' ) { + itemIcon = footer; + } else if ( categoryId === 'uncategorized' ) { + itemIcon = symbolFilled; + } + return ( <>
@@ -127,7 +142,7 @@ export default function GridItem( { categoryId, composite, icon, item } ) { spacing={ 3 } className="edit-site-library__pattern-title" > - { icon && } + { icon && } { item.title } { item.type === USER_PATTERNS && ( From 6771ba4c5335801da18c3d41aa025365910e6234 Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Wed, 28 Jun 2023 09:34:28 -0400 Subject: [PATCH 063/266] fix: Display heading level dropdown icons and labels (#52004) * fix: Heading level dropdown displays active selection A recent refactor for web was only partially applied to the sibling native files. This finishes the application so that the active selection is displayed, passed through via the `value` prop. * fix: Heading level dropdown leverages Icon Without using Icon, the SVG elements rendered without a width, causing layout issues and invisible icons. --- .../heading-level-icon.js | 7 ++++++- .../index.native.js | 12 ++++++++---- .../src/heading/test/index.native.js | 18 ++++++++++++++++++ .../src/mobile/bottom-sheet/cell.native.js | 1 + 4 files changed, 33 insertions(+), 5 deletions(-) diff --git a/packages/block-editor/src/components/block-heading-level-dropdown/heading-level-icon.js b/packages/block-editor/src/components/block-heading-level-dropdown/heading-level-icon.js index 7b161d9c01700e..dfdc7f824dfbc8 100644 --- a/packages/block-editor/src/components/block-heading-level-dropdown/heading-level-icon.js +++ b/packages/block-editor/src/components/block-heading-level-dropdown/heading-level-icon.js @@ -10,6 +10,7 @@ import { headingLevel6, paragraph, } from '@wordpress/icons'; +import { Icon } from '@wordpress/components'; /** @typedef {import('@wordpress/element').WPComponent} WPComponent */ @@ -39,5 +40,9 @@ const LEVEL_TO_PATH = { * @return {?WPComponent} The icon. */ export default function HeadingLevelIcon( { level } ) { - return LEVEL_TO_PATH[ level ] ?? null; + if ( LEVEL_TO_PATH[ level ] ) { + return ; + } + + return null; } diff --git a/packages/block-editor/src/components/block-heading-level-dropdown/index.native.js b/packages/block-editor/src/components/block-heading-level-dropdown/index.native.js index 1ba451ff54b8f4..fecb608e692e27 100644 --- a/packages/block-editor/src/components/block-heading-level-dropdown/index.native.js +++ b/packages/block-editor/src/components/block-heading-level-dropdown/index.native.js @@ -30,7 +30,11 @@ const HEADING_LEVELS = [ 1, 2, 3, 4, 5, 6 ]; * * @return {WPComponent} The toolbar. */ -export default function HeadingLevelDropdown( { selectedLevel, onChange } ) { +export default function HeadingLevelDropdown( { + options = HEADING_LEVELS, + value, + onChange, +} ) { const createLevelControl = ( targetLevel, currentLevel, @@ -53,9 +57,9 @@ export default function HeadingLevelDropdown( { selectedLevel, onChange } ) { return ( } - controls={ HEADING_LEVELS.map( ( index ) => - createLevelControl( index, selectedLevel, onChange ) + icon={ } + controls={ options.map( ( index ) => + createLevelControl( index, value, onChange ) ) } label={ __( 'Change level' ) } /> diff --git a/packages/block-library/src/heading/test/index.native.js b/packages/block-library/src/heading/test/index.native.js index 34fdc00c032ed8..493ef7f84a79d6 100644 --- a/packages/block-library/src/heading/test/index.native.js +++ b/packages/block-library/src/heading/test/index.native.js @@ -112,4 +112,22 @@ describe( 'Heading block', () => { // Assert expect( getEditorHtml() ).toMatchSnapshot(); } ); + + it( 'change level dropdown displays active selection', async () => { + // Arrange + const screen = await initializeEditor(); + await addBlock( screen, 'Heading' ); + const headingBlock = await getBlock( screen, 'Heading' ); + + // Act + fireEvent.press( headingBlock ); + fireEvent.press( screen.getByLabelText( 'Change level' ) ); + + // Assert + expect( + within( screen.getByLabelText( 'Heading 2' ) ).getByTestId( + 'bottom-sheet-cell-selected-icon' + ) + ).toBeVisible(); + } ); } ); diff --git a/packages/components/src/mobile/bottom-sheet/cell.native.js b/packages/components/src/mobile/bottom-sheet/cell.native.js index c29025e81d79b3..30f061dc5c14b3 100644 --- a/packages/components/src/mobile/bottom-sheet/cell.native.js +++ b/packages/components/src/mobile/bottom-sheet/cell.native.js @@ -431,6 +431,7 @@ class BottomSheetCell extends Component { ) } { showValue && getValueComponent() } From 61a516017c6b409443414c892748e890d6214939 Mon Sep 17 00:00:00 2001 From: Marin Atanasov <8436925+tyxla@users.noreply.github.com> Date: Wed, 28 Jun 2023 17:23:58 +0300 Subject: [PATCH 064/266] Lodash: Refactor away from `_.kebabCase()` in generic template modal (#51910) * Lodash: Refactor away from _.kebabCase() in generic template modal * Deprecate _.kebabCase() --- .eslintrc.js | 1 + .../add-custom-generic-template-modal-content.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.eslintrc.js b/.eslintrc.js index 2b31cc509dd2cd..9933b831e4725a 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -100,6 +100,7 @@ const restrictedImports = [ 'isPlainObject', 'isString', 'isUndefined', + 'kebabCase', 'keyBy', 'keys', 'last', diff --git a/packages/edit-site/src/components/add-new-template/add-custom-generic-template-modal-content.js b/packages/edit-site/src/components/add-new-template/add-custom-generic-template-modal-content.js index 9610ad1d4c3a47..345d26a60e659d 100644 --- a/packages/edit-site/src/components/add-new-template/add-custom-generic-template-modal-content.js +++ b/packages/edit-site/src/components/add-new-template/add-custom-generic-template-modal-content.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { kebabCase } from 'lodash'; +import { paramCase as kebabCase } from 'change-case'; /** * WordPress dependencies From 09210ef5cd2a0169bccab0e55e56c3ecf88a68c1 Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Wed, 28 Jun 2023 17:05:04 +0200 Subject: [PATCH 065/266] hasResolvingSelector: exclude from result of resolveSelect (#52038) --- packages/data/src/redux-store/index.js | 1 + packages/data/src/redux-store/test/index.js | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/data/src/redux-store/index.js b/packages/data/src/redux-store/index.js index 933cd11f3ddba1..583a1ebed1dbba 100644 --- a/packages/data/src/redux-store/index.js +++ b/packages/data/src/redux-store/index.js @@ -422,6 +422,7 @@ function mapResolveSelectors( selectors, store ) { getCachedResolvers, getResolutionState, getResolutionError, + hasResolvingSelectors, ...storeSelectors } = selectors; diff --git a/packages/data/src/redux-store/test/index.js b/packages/data/src/redux-store/test/index.js index e3cc2c727dbc5f..daca128480daeb 100644 --- a/packages/data/src/redux-store/test/index.js +++ b/packages/data/src/redux-store/test/index.js @@ -281,7 +281,6 @@ describe( 'resolveSelect', () => { it( 'returns only store native selectors and excludes all meta ones', () => { expect( Object.keys( registry.resolveSelect( 'store' ) ) ).toEqual( [ - 'hasResolvingSelectors', 'getItems', 'getItemsNoResolver', ] ); From f4ed553c0c25bb09f482a5062a8caad46072465f Mon Sep 17 00:00:00 2001 From: James Koster Date: Wed, 28 Jun 2023 16:36:54 +0100 Subject: [PATCH 066/266] Update home template name (#52048) --- lib/compat/wordpress-6.3/block-template-utils.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compat/wordpress-6.3/block-template-utils.php b/lib/compat/wordpress-6.3/block-template-utils.php index cdd870db2d0813..cc367f66dd42d9 100644 --- a/lib/compat/wordpress-6.3/block-template-utils.php +++ b/lib/compat/wordpress-6.3/block-template-utils.php @@ -29,7 +29,7 @@ function gutenberg_get_default_block_template_types( $default_template_types ) { } if ( isset( $default_template_types['home'] ) ) { $default_template_types['home'] = array( - 'title' => _x( 'Home', 'Template name', 'gutenberg' ), + 'title' => _x( 'Blog Home', 'Template name', 'gutenberg' ), 'description' => __( 'Displays the latest posts as either the site homepage or as the "Posts page" as defined under reading settings. If it exists, the Front Page template overrides this template when posts are shown on the homepage.', 'gutenberg' From 9b8d5c1dc353872f98fd1a3fca811e00a45c0de3 Mon Sep 17 00:00:00 2001 From: David Arenas Date: Wed, 28 Jun 2023 18:00:23 +0200 Subject: [PATCH 067/266] Create @wordpress/interactivity with the Interactivity API (#50906) * Start with package.json and README * Add new package to docs/manifest.json * Copy-paste runtime files from block-library * Add .npmrc file * Add interactivity package to dependencies * Create a custom webpack config for interactivity * Expose interactivity runtime in `wp.interactivity` * Update package-lock * Add `@wordpress/interactivity` to block-library deps * Rename entry point to index * Remove vendors chunk * Add oddly required aliases * Add view prefix to interactivity.js files * Use view-interactivity files when enabled * Stop adding defer to Interactivity scripts * Remove webpack config for interactivity.js files * Remove interactivity runtime from block-library * Remove interactivity runtime from sideEffects * Undo temporary fix for Interactivity API in dependency-extraction-webpack-plugin * Remove script loader for Interactivity API runtime * Remove block-librar/interactivity from build_files * Add src/index.js to Interactivity API entry file * Remove unnecessary aliases * Restore data-wp-body directive * Interactivity API: add `wp_store` (#51191) * Add `wp_store` to the Interactivity API * Rename WP_Interactivity_Store and move filter to scripts file * Remove todos to change the store id * Rename syntax to -- and data-wp-interactive (#51241) * Interactivity API: initial support for SSR (#51229) * Initial version working with basic support for wp-bind * Add wp-context * Add wp-class * Add wp-style * Add wp-text * Add directive processing tests * Add WP_Directive_Processor class tests * Add wp-bind tests * Add wp-context tests * Add wp-class tests * Add wp-style tests * Add wp-text tests * Add evaluate tests * Fix PHP lint * Prevent errors with incorrect JSON objects * Add support for functions in the server * Remove require for missing script-loader.php * Remove missing PHP file * Rename view file and fix block.json * Add "interactivity" to supports and fix renaming * Code improvements for the SSR part of the Interactivity API (#51640) * Fix multi-line comments and add examples * Add parse_attribute_name static method to WP_Directive_Processor * Replace array functions with a foreach loop * Add explanatory comment for the negation operator check * Replace $array with $path_segments * Minor fix for the negation operator comment * Call only instances of Closure * Improve negation operator code style * Do not lower-case tags * Use static parse_attribute_name inside directive processors * Add basic error handling in wp-context * Fix hidden identation errors * Use the correct variable name * Fix test for evaluating functions * Remove references to "attribute" directives * Remove emtpy lines in multi-line function calls * Fix typo --------- Co-authored-by: Luis Herranz * Add the full Interactivity API runtime (but removing the client-side navigation). (#51194) * Add show and text directives * Move directive bind tests * Move the rest of e2e tests (except csn-related) * Add interactive-blocks plugin for e2e tests * Move test plugins one folder up * Add plugin to .wp-env.json * Change directive-bind spec file to use new plugin * Move plugin to e2e-tests package * Move HTML for directive-bind to plugin * Update exposed properties from preact * Refactor directive-bind spec file * Create directive-effect block for e2e testing * Update directive-effect spec file * Remove unnecessary files * Fix e2e tests for bind and effect directives * Refactor fixtures and use them for bind and effect * Remove unnecessary editorScript * Fix e2e test for directive priorities * Remove unnecessary files * Fix negation operator * Refactor store-tag e2e tests * Refactor directive-class e2e tests * Remove extra spaces * Add util for removing all created posts * Add block for context directive * Add block for directive show testing * Remove unintentionally added artifact * Ignore artifacts generated inside /test/e2e * Remove unused html * Add block for directive text testing * Add blocks for tovdom testing * Update directives syntax in e2e tests * Add getLink to InteractivityUtils * Fix php lint errors * Add disable_directives_ssr param * Fix phpcs errors * Fix missing phpcs error and warnings * Remove `wp-interactivity` from `viewScript` --------- Co-authored-by: Luis Herranz * Remove custom watchOptions in interactivity webpack config * Update version and description of interactivity package --------- Co-authored-by: Luis Herranz --- .gitignore | 1 + bin/build-plugin-zip.sh | 1 - docs/manifest.json | 6 + docs/reference-guides/core-blocks.md | 4 +- lib/experimental/interactivity-api/blocks.php | 5 +- .../class-wp-directive-context.php | 77 ++++++ .../class-wp-directive-processor.php | 193 +++++++++++++++ .../class-wp-interactivity-store.php | 73 ++++++ .../directive-processing.php | 170 +++++++++++++ .../interactivity-api/directives/wp-bind.php | 32 +++ .../interactivity-api/directives/wp-class.php | 36 +++ .../directives/wp-context.php | 33 +++ .../interactivity-api/directives/wp-style.php | 72 ++++++ .../interactivity-api/directives/wp-text.php | 27 +++ .../interactivity-api/script-loader.php | 56 ----- .../interactivity-api/scripts.php | 28 +++ lib/experimental/interactivity-api/store.php | 26 ++ lib/load.php | 13 +- package-lock.json | 9 + package.json | 1 + packages/block-library/package.json | 4 +- packages/block-library/src/file/block.json | 3 +- packages/block-library/src/file/index.php | 17 -- .../block-library/src/file/interactivity.js | 15 -- packages/block-library/src/file/view.js | 19 +- ...interactivity.js => view-interactivity.js} | 4 +- .../block-library/src/navigation/block.json | 5 +- .../block-library/src/navigation/index.php | 15 -- .../src/navigation/interactivity.js | 169 ------------- .../src/navigation/view-modal.js | 78 ------ packages/block-library/src/navigation/view.js | 229 +++++++++++++----- .../lib/index.js | 10 - .../src/request-utils/posts.ts | 1 + .../e2e-tests/plugins/interactive-blocks.php | 48 ++++ .../directive-bind/block.json | 14 ++ .../directive-bind/render.php | 59 +++++ .../interactive-blocks/directive-bind/view.js | 23 ++ .../directive-class/block.json | 14 ++ .../directive-class/render.php | 75 ++++++ .../directive-class/view.js | 21 ++ .../directive-context/block.json | 14 ++ .../directive-context/render.php | 121 +++++++++ .../directive-context/view.js | 22 ++ .../directive-effect/block.json | 14 ++ .../directive-effect/render.php | 27 +++ .../directive-effect/view.js | 61 +++++ .../directive-priorities/block.json | 14 ++ .../directive-priorities/render.php | 20 ++ .../directive-priorities/view.js | 121 +++++++++ .../directive-show/block.json | 14 ++ .../directive-show/render.php | 53 ++++ .../interactive-blocks/directive-show/view.js | 24 ++ .../directive-text/block.json | 14 ++ .../directive-text/render.php | 35 +++ .../interactive-blocks/directive-text/view.js | 17 ++ .../negation-operator/block.json | 14 ++ .../negation-operator/render.php | 26 ++ .../negation-operator/view.js | 22 ++ .../interactive-blocks/store-tag/block.json | 14 ++ .../interactive-blocks/store-tag/render.php | 64 +++++ .../interactive-blocks/store-tag/view.js | 24 ++ .../tovdom-islands/block.json | 14 ++ .../tovdom-islands/render.php | 66 +++++ .../interactive-blocks/tovdom-islands/view.js | 9 + .../interactive-blocks/tovdom/block.json | 14 ++ .../interactive-blocks/tovdom/cdata.js | 15 ++ .../tovdom/processing-instructions.js | 16 ++ .../interactive-blocks/tovdom/render.php | 33 +++ .../plugins/interactive-blocks/tovdom/view.js | 5 + packages/interactivity/.npmrc | 1 + packages/interactivity/README.md | 1 + packages/interactivity/package.json | 35 +++ .../src}/constants.js | 0 .../src}/directives.js | 43 +++- .../src}/hooks.js | 0 .../src}/hydration.js | 0 .../src}/index.js | 4 + .../src}/portals.js | 0 .../src}/store.js | 3 +- .../src}/utils.js | 0 .../src}/vdom.js | 0 .../class-wp-directive-processor-test.php | 132 ++++++++++ .../class-wp-interactivity-store-test.php | 168 +++++++++++++ .../directive-processing-test.php | 170 +++++++++++++ .../directives/wp-bind-test.php | 46 ++++ .../directives/wp-class-test.php | 98 ++++++++ .../directives/wp-context-test.php | 77 ++++++ .../directives/wp-style-test.php | 46 ++++ .../directives/wp-text-test.php | 45 ++++ .../interactivity/directive-bind.spec.ts | 96 ++++++++ .../interactivity/directive-effect.spec.ts | 39 +++ .../directive-priorities.spec.ts | 84 +++++++ .../interactivity/directives-class.spec.ts | 101 ++++++++ .../interactivity/directives-context.spec.ts | 165 +++++++++++++ .../interactivity/directives-show.spec.ts | 59 +++++ .../interactivity/directives-text.spec.ts | 36 +++ .../e2e/specs/interactivity/fixtures/index.ts | 25 ++ .../fixtures/interactivity-utils.ts | 60 +++++ .../interactivity/negation-operator.spec.ts | 44 ++++ .../e2e/specs/interactivity/store-tag.spec.ts | 86 +++++++ .../interactivity/tovdom-islands.spec.ts | 58 +++++ test/e2e/specs/interactivity/tovdom.spec.ts | 55 +++++ tools/webpack/blocks.js | 78 ------ tools/webpack/interactivity.js | 56 +++++ tools/webpack/packages.js | 3 +- webpack.config.js | 8 +- 106 files changed, 3983 insertions(+), 532 deletions(-) create mode 100644 lib/experimental/interactivity-api/class-wp-directive-context.php create mode 100644 lib/experimental/interactivity-api/class-wp-directive-processor.php create mode 100644 lib/experimental/interactivity-api/class-wp-interactivity-store.php create mode 100644 lib/experimental/interactivity-api/directive-processing.php create mode 100644 lib/experimental/interactivity-api/directives/wp-bind.php create mode 100644 lib/experimental/interactivity-api/directives/wp-class.php create mode 100644 lib/experimental/interactivity-api/directives/wp-context.php create mode 100644 lib/experimental/interactivity-api/directives/wp-style.php create mode 100644 lib/experimental/interactivity-api/directives/wp-text.php delete mode 100644 lib/experimental/interactivity-api/script-loader.php create mode 100644 lib/experimental/interactivity-api/scripts.php create mode 100644 lib/experimental/interactivity-api/store.php delete mode 100644 packages/block-library/src/file/interactivity.js rename packages/block-library/src/image/{interactivity.js => view-interactivity.js} (99%) delete mode 100644 packages/block-library/src/navigation/interactivity.js delete mode 100644 packages/block-library/src/navigation/view-modal.js create mode 100644 packages/e2e-tests/plugins/interactive-blocks.php create mode 100644 packages/e2e-tests/plugins/interactive-blocks/directive-bind/block.json create mode 100644 packages/e2e-tests/plugins/interactive-blocks/directive-bind/render.php create mode 100644 packages/e2e-tests/plugins/interactive-blocks/directive-bind/view.js create mode 100644 packages/e2e-tests/plugins/interactive-blocks/directive-class/block.json create mode 100644 packages/e2e-tests/plugins/interactive-blocks/directive-class/render.php create mode 100644 packages/e2e-tests/plugins/interactive-blocks/directive-class/view.js create mode 100644 packages/e2e-tests/plugins/interactive-blocks/directive-context/block.json create mode 100644 packages/e2e-tests/plugins/interactive-blocks/directive-context/render.php create mode 100644 packages/e2e-tests/plugins/interactive-blocks/directive-context/view.js create mode 100644 packages/e2e-tests/plugins/interactive-blocks/directive-effect/block.json create mode 100644 packages/e2e-tests/plugins/interactive-blocks/directive-effect/render.php create mode 100644 packages/e2e-tests/plugins/interactive-blocks/directive-effect/view.js create mode 100644 packages/e2e-tests/plugins/interactive-blocks/directive-priorities/block.json create mode 100644 packages/e2e-tests/plugins/interactive-blocks/directive-priorities/render.php create mode 100644 packages/e2e-tests/plugins/interactive-blocks/directive-priorities/view.js create mode 100644 packages/e2e-tests/plugins/interactive-blocks/directive-show/block.json create mode 100644 packages/e2e-tests/plugins/interactive-blocks/directive-show/render.php create mode 100644 packages/e2e-tests/plugins/interactive-blocks/directive-show/view.js create mode 100644 packages/e2e-tests/plugins/interactive-blocks/directive-text/block.json create mode 100644 packages/e2e-tests/plugins/interactive-blocks/directive-text/render.php create mode 100644 packages/e2e-tests/plugins/interactive-blocks/directive-text/view.js create mode 100644 packages/e2e-tests/plugins/interactive-blocks/negation-operator/block.json create mode 100644 packages/e2e-tests/plugins/interactive-blocks/negation-operator/render.php create mode 100644 packages/e2e-tests/plugins/interactive-blocks/negation-operator/view.js create mode 100644 packages/e2e-tests/plugins/interactive-blocks/store-tag/block.json create mode 100644 packages/e2e-tests/plugins/interactive-blocks/store-tag/render.php create mode 100644 packages/e2e-tests/plugins/interactive-blocks/store-tag/view.js create mode 100644 packages/e2e-tests/plugins/interactive-blocks/tovdom-islands/block.json create mode 100644 packages/e2e-tests/plugins/interactive-blocks/tovdom-islands/render.php create mode 100644 packages/e2e-tests/plugins/interactive-blocks/tovdom-islands/view.js create mode 100644 packages/e2e-tests/plugins/interactive-blocks/tovdom/block.json create mode 100644 packages/e2e-tests/plugins/interactive-blocks/tovdom/cdata.js create mode 100644 packages/e2e-tests/plugins/interactive-blocks/tovdom/processing-instructions.js create mode 100644 packages/e2e-tests/plugins/interactive-blocks/tovdom/render.php create mode 100644 packages/e2e-tests/plugins/interactive-blocks/tovdom/view.js create mode 100644 packages/interactivity/.npmrc create mode 100644 packages/interactivity/README.md create mode 100644 packages/interactivity/package.json rename packages/{block-library/src/utils/interactivity => interactivity/src}/constants.js (100%) rename packages/{block-library/src/utils/interactivity => interactivity/src}/directives.js (88%) rename packages/{block-library/src/utils/interactivity => interactivity/src}/hooks.js (100%) rename packages/{block-library/src/utils/interactivity => interactivity/src}/hydration.js (100%) rename packages/{block-library/src/utils/interactivity => interactivity/src}/index.js (61%) rename packages/{block-library/src/utils/interactivity => interactivity/src}/portals.js (100%) rename packages/{block-library/src/utils/interactivity => interactivity/src}/store.js (92%) rename packages/{block-library/src/utils/interactivity => interactivity/src}/utils.js (100%) rename packages/{block-library/src/utils/interactivity => interactivity/src}/vdom.js (100%) create mode 100644 phpunit/experimental/interactivity-api/class-wp-directive-processor-test.php create mode 100644 phpunit/experimental/interactivity-api/class-wp-interactivity-store-test.php create mode 100644 phpunit/experimental/interactivity-api/directive-processing-test.php create mode 100644 phpunit/experimental/interactivity-api/directives/wp-bind-test.php create mode 100644 phpunit/experimental/interactivity-api/directives/wp-class-test.php create mode 100644 phpunit/experimental/interactivity-api/directives/wp-context-test.php create mode 100644 phpunit/experimental/interactivity-api/directives/wp-style-test.php create mode 100644 phpunit/experimental/interactivity-api/directives/wp-text-test.php create mode 100644 test/e2e/specs/interactivity/directive-bind.spec.ts create mode 100644 test/e2e/specs/interactivity/directive-effect.spec.ts create mode 100644 test/e2e/specs/interactivity/directive-priorities.spec.ts create mode 100644 test/e2e/specs/interactivity/directives-class.spec.ts create mode 100644 test/e2e/specs/interactivity/directives-context.spec.ts create mode 100644 test/e2e/specs/interactivity/directives-show.spec.ts create mode 100644 test/e2e/specs/interactivity/directives-text.spec.ts create mode 100644 test/e2e/specs/interactivity/fixtures/index.ts create mode 100644 test/e2e/specs/interactivity/fixtures/interactivity-utils.ts create mode 100644 test/e2e/specs/interactivity/negation-operator.spec.ts create mode 100644 test/e2e/specs/interactivity/store-tag.spec.ts create mode 100644 test/e2e/specs/interactivity/tovdom-islands.spec.ts create mode 100644 test/e2e/specs/interactivity/tovdom.spec.ts create mode 100644 tools/webpack/interactivity.js diff --git a/.gitignore b/.gitignore index 4a7f4708ce399a..19e43aecea7b82 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ coverage *.log yarn.lock /artifacts +/test/e2e/artifacts /perf-envs /composer.lock diff --git a/bin/build-plugin-zip.sh b/bin/build-plugin-zip.sh index 131e434d1383d0..4ba931c4a4aeb6 100755 --- a/bin/build-plugin-zip.sh +++ b/bin/build-plugin-zip.sh @@ -83,7 +83,6 @@ build_files=$( build/block-library/blocks/*.php \ build/block-library/blocks/*/block.json \ build/block-library/blocks/*/*.{js,js.map,css,asset.php} \ - build/block-library/interactivity/*.{js,js.map,asset.php} \ build/edit-widgets/blocks/*/block.json \ build/widgets/blocks/*.php \ build/widgets/blocks/*/block.json \ diff --git a/docs/manifest.json b/docs/manifest.json index 8a21bc38a54189..aaadd15a570790 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -1673,6 +1673,12 @@ "markdown_source": "../packages/icons/README.md", "parent": "packages" }, + { + "title": "@wordpress/interactivity", + "slug": "packages-interactivity", + "markdown_source": "../packages/interactivity/README.md", + "parent": "packages" + }, { "title": "@wordpress/interface", "slug": "packages-interface", diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index 0306e026b5f45a..939cc6748ec91c 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -266,7 +266,7 @@ Add a link to a downloadable file. ([Source](https://github.com/WordPress/gutenb - **Name:** core/file - **Category:** media -- **Supports:** align, anchor, color (background, gradients, link, ~~text~~) +- **Supports:** align, anchor, color (background, gradients, link, ~~text~~), interactivity - **Attributes:** displayPreview, downloadButtonText, fileId, fileName, href, id, previewHeight, showDownloadButton, textLinkHref, textLinkTarget ## Footnotes @@ -421,7 +421,7 @@ A collection of blocks that allow visitors to get around your site. ([Source](ht - **Name:** core/navigation - **Category:** theme -- **Supports:** align (full, wide), inserter, layout (allowSizingOnChildren, default, ~~allowInheriting~~, ~~allowSwitching~~, ~~allowVerticalAlignment~~), spacing (blockGap, units), typography (fontSize, lineHeight), ~~html~~ +- **Supports:** align (full, wide), inserter, interactivity, layout (allowSizingOnChildren, default, ~~allowInheriting~~, ~~allowSwitching~~, ~~allowVerticalAlignment~~), spacing (blockGap, units), typography (fontSize, lineHeight), ~~html~~ - **Attributes:** __unstableLocation, backgroundColor, customBackgroundColor, customOverlayBackgroundColor, customOverlayTextColor, customTextColor, hasIcon, icon, maxNestingLevel, openSubmenusOnClick, overlayBackgroundColor, overlayMenu, overlayTextColor, ref, rgbBackgroundColor, rgbTextColor, showSubmenuIcon, templateLock, textColor ## Custom Link diff --git a/lib/experimental/interactivity-api/blocks.php b/lib/experimental/interactivity-api/blocks.php index 0087f95cbf1449..76955357e6c931 100644 --- a/lib/experimental/interactivity-api/blocks.php +++ b/lib/experimental/interactivity-api/blocks.php @@ -2,7 +2,8 @@ /** * Extend WordPress core blocks to use the Interactivity API. * - * @package gutenberg + * @package Gutenberg + * @subpackage Interactivity API */ /** @@ -17,7 +18,7 @@ function gutenberg_block_update_interactive_view_script( $metadata ) { in_array( $metadata['name'], array( 'core/image' ), true ) && str_contains( $metadata['file'], 'build/block-library/blocks' ) ) { - $metadata['viewScript'] = array( 'file:./interactivity.min.js' ); + $metadata['viewScript'] = array( 'file:./view-interactivity.min.js' ); } return $metadata; } diff --git a/lib/experimental/interactivity-api/class-wp-directive-context.php b/lib/experimental/interactivity-api/class-wp-directive-context.php new file mode 100644 index 00000000000000..7186922d137a89 --- /dev/null +++ b/lib/experimental/interactivity-api/class-wp-directive-context.php @@ -0,0 +1,77 @@ + + * + *
+ * + *
+ * + *
+ */ +class WP_Directive_Context { + /** + * The stack used to store contexts internally. + * + * @var array An array of contexts. + */ + protected $stack = array( array() ); + + /** + * Constructor. + * + * Accepts a context as an argument to initialize this with. + * + * @param array $context A context. + */ + function __construct( $context = array() ) { + $this->set_context( $context ); + } + + /** + * Return the current context. + * + * @return array The current context. + */ + public function get_context() { + return end( $this->stack ); + } + + /** + * Set the current context. + * + * @param array $context The context to be set. + * + * @return void + */ + public function set_context( $context ) { + if ( $context ) { + array_push( $this->stack, array_replace_recursive( $this->get_context(), $context ) ); + } + } + + /** + * Reset the context to its previous state. + * + * @return void + */ + public function rewind_context() { + array_pop( $this->stack ); + } +} diff --git a/lib/experimental/interactivity-api/class-wp-directive-processor.php b/lib/experimental/interactivity-api/class-wp-directive-processor.php new file mode 100644 index 00000000000000..608466a9c6edae --- /dev/null +++ b/lib/experimental/interactivity-api/class-wp-directive-processor.php @@ -0,0 +1,193 @@ +get_tag(); + + if ( self::is_html_void_element( $tag_name ) ) { + return false; + } + + while ( $this->next_tag( + array( + 'tag_name' => $tag_name, + 'tag_closers' => 'visit', + ) + ) ) { + if ( ! $this->is_tag_closer() ) { + $depth++; + continue; + } + + if ( 0 === $depth ) { + return true; + } + + $depth--; + } + + return false; + } + + /** + * Return the content between two balanced tags. + * + * When called on an opening tag, return the HTML content found between that + * opening tag and its matching closing tag. + * + * @return string The content between the current opening and its matching + * closing tag. + */ + public function get_inner_html() { + $bookmarks = $this->get_balanced_tag_bookmarks(); + if ( ! $bookmarks ) { + return false; + } + list( $start_name, $end_name ) = $bookmarks; + + $start = $this->bookmarks[ $start_name ]->end + 1; + $end = $this->bookmarks[ $end_name ]->start; + + $this->seek( $start_name ); // Return to original position. + $this->release_bookmark( $start_name ); + $this->release_bookmark( $end_name ); + + return substr( $this->html, $start, $end - $start ); + } + + /** + * Set the content between two balanced tags. + * + * When called on an opening tag, set the HTML content found between that + * opening tag and its matching closing tag. + * + * @param string $new_html The string to replace the content between the + * matching tags with. + * + * @return bool Whether the content was successfully replaced. + */ + public function set_inner_html( $new_html ) { + $this->get_updated_html(); // Apply potential previous updates. + + $bookmarks = $this->get_balanced_tag_bookmarks(); + if ( ! $bookmarks ) { + return false; + } + list( $start_name, $end_name ) = $bookmarks; + + $start = $this->bookmarks[ $start_name ]->end + 1; + $end = $this->bookmarks[ $end_name ]->start; + + $this->seek( $start_name ); // Return to original position. + $this->release_bookmark( $start_name ); + $this->release_bookmark( $end_name ); + + $this->lexical_updates[] = new WP_HTML_Text_Replacement( $start, $end, $new_html ); + return true; + } + + /** + * Return a pair of bookmarks for the current opening tag and the matching + * closing tag. + * + * @return array|false A pair of bookmarks, or false if there's no matching + * closing tag. + */ + public function get_balanced_tag_bookmarks() { + $i = 0; + while ( array_key_exists( 'start' . $i, $this->bookmarks ) ) { + ++$i; + } + $start_name = 'start' . $i; + + $this->set_bookmark( $start_name ); + if ( ! $this->next_balanced_closer() ) { + $this->release_bookmark( $start_name ); + return false; + } + + $i = 0; + while ( array_key_exists( 'end' . $i, $this->bookmarks ) ) { + ++$i; + } + $end_name = 'end' . $i; + $this->set_bookmark( $end_name ); + + return array( $start_name, $end_name ); + } + + /** + * Whether a given HTML element is void (e.g.
). + * + * @param string $tag_name The element in question. + * @return bool True if the element is void. + * + * @see https://html.spec.whatwg.org/#elements-2 + */ + public static function is_html_void_element( $tag_name ) { + switch ( $tag_name ) { + case 'AREA': + case 'BASE': + case 'BR': + case 'COL': + case 'EMBED': + case 'HR': + case 'IMG': + case 'INPUT': + case 'LINK': + case 'META': + case 'SOURCE': + case 'TRACK': + case 'WBR': + return true; + + default: + return false; + } + } + + /** + * Extract and return the directive type and the the part after the double + * hyphen from an attribute name (if present), in an array format. + * + * Examples: + * + * 'wp-island' => array( 'wp-island', null ) + * 'wp-bind--src' => array( 'wp-bind', 'src' ) + * 'wp-thing--and--thang' => array( 'wp-thing', 'and--thang' ) + * + * @param string $name The attribute name. + * @return array The resulting array + */ + public static function parse_attribute_name( $name ) { + return explode( '--', $name, 2 ); + } +} diff --git a/lib/experimental/interactivity-api/class-wp-interactivity-store.php b/lib/experimental/interactivity-api/class-wp-interactivity-store.php new file mode 100644 index 00000000000000..46ca574e667c4c --- /dev/null +++ b/lib/experimental/interactivity-api/class-wp-interactivity-store.php @@ -0,0 +1,73 @@ +$store"; + } +} diff --git a/lib/experimental/interactivity-api/directive-processing.php b/lib/experimental/interactivity-api/directive-processing.php new file mode 100644 index 00000000000000..0f2af5da0f8044 --- /dev/null +++ b/lib/experimental/interactivity-api/directive-processing.php @@ -0,0 +1,170 @@ + 'gutenberg_interactivity_process_wp_bind', + 'data-wp-context' => 'gutenberg_interactivity_process_wp_context', + 'data-wp-class' => 'gutenberg_interactivity_process_wp_class', + 'data-wp-style' => 'gutenberg_interactivity_process_wp_style', + 'data-wp-text' => 'gutenberg_interactivity_process_wp_text', + ); + + $tags = new WP_Directive_Processor( $block_content ); + $tags = gutenberg_interactivity_process_directives( $tags, 'data-wp-', $directives ); + return $tags->get_updated_html(); +} +add_filter( 'render_block', 'gutenberg_interactivity_process_directives_in_root_blocks', 10, 2 ); + +/** + * Mark the inner blocks with a temporary property so we can discard them later, + * and process only the root blocks. + * + * @param array $parsed_block The parsed block. + * @param array $source_block The source block. + * @param array $parent_block The parent block. + * + * @return array The parsed block. + */ +function gutenberg_interactivity_mark_inner_blocks( $parsed_block, $source_block, $parent_block ) { + if ( isset( $parent_block ) ) { + $parsed_block['is_inner_block'] = true; + } + return $parsed_block; +} +add_filter( 'render_block_data', 'gutenberg_interactivity_mark_inner_blocks', 10, 3 ); + +/** + * Process directives. + * + * @param WP_Directive_Processor $tags An instance of the WP_Directive_Processor. + * @param string $prefix Attribute prefix. + * @param string[] $directives Directives. + * + * @return WP_Directive_Processor The modified instance of the + * WP_Directive_Processor. + */ +function gutenberg_interactivity_process_directives( $tags, $prefix, $directives ) { + $context = new WP_Directive_Context; + $tag_stack = array(); + + while ( $tags->next_tag( array( 'tag_closers' => 'visit' ) ) ) { + $tag_name = $tags->get_tag(); + + // Is this a tag that closes the latest opening tag? + if ( $tags->is_tag_closer() ) { + if ( 0 === count( $tag_stack ) ) { + continue; + } + + list( $latest_opening_tag_name, $attributes ) = end( $tag_stack ); + if ( $latest_opening_tag_name === $tag_name ) { + array_pop( $tag_stack ); + + // If the matching opening tag didn't have any directives, we move on. + if ( 0 === count( $attributes ) ) { + continue; + } + } + } else { + $attributes = array(); + foreach ( $tags->get_attribute_names_with_prefix( $prefix ) as $name ) { + /* + * Removes the part after the double hyphen before looking for + * the directive processor inside `$directives`, e.g., "wp-bind" + * from "wp-bind--src" and "wp-context" from "wp-context" etc... + */ + list( $type ) = WP_Directive_Processor::parse_attribute_name( $name ); + if ( array_key_exists( $type, $directives ) ) { + $attributes[] = $type; + } + } + + /* + * If this is an open tag, and if it either has directives, or if + * we're inside a tag that does, take note of this tag and its + * directives so we can call its directive processor once we + * encounter the matching closing tag. + */ + if ( + ! WP_Directive_Processor::is_html_void_element( $tags->get_tag() ) && + ( 0 !== count( $attributes ) || 0 !== count( $tag_stack ) ) + ) { + $tag_stack[] = array( $tag_name, $attributes ); + } + } + + foreach ( $attributes as $attribute ) { + call_user_func( $directives[ $attribute ], $tags, $context ); + } + } + + return $tags; +} + +/** + * Resolve the reference using the store and the context from the provided path. + * + * @param string $path Path. + * @param array $context Context data. + * @return mixed + */ +function gutenberg_interactivity_evaluate_reference( $path, array $context = array() ) { + $store = array_merge( + WP_Interactivity_Store::get_data(), + array( 'context' => $context ) + ); + + /* + * Check first if the directive path is preceded by a negator operator (!), + * indicating that the value obtained from the Interactivity Store (or the + * passed context) using the subsequent path should be negated. + */ + $should_negate_value = '!' === $path[0]; + + $path = $should_negate_value ? substr( $path, 1 ) : $path; + $path_segments = explode( '.', $path ); + $current = $store; + foreach ( $path_segments as $p ) { + if ( isset( $current[ $p ] ) ) { + $current = $current[ $p ]; + } else { + return null; + } + } + + /* + * Check if $current is an anonymous function or an arrow function, and if + * so, call it passing the store. Other types of callables are ignored on + * purpose, as arbitrary strings or arrays could be wrongly evaluated as + * "callables". + * + * E.g., "file" is an string and a "callable" (the "file" function exists). + */ + if ( $current instanceof Closure ) { + $current = call_user_func( $current, $store ); + } + + // Return the opposite if it has a negator operator (!). + return $should_negate_value ? ! $current : $current; +} diff --git a/lib/experimental/interactivity-api/directives/wp-bind.php b/lib/experimental/interactivity-api/directives/wp-bind.php new file mode 100644 index 00000000000000..54be4a9faeb7d2 --- /dev/null +++ b/lib/experimental/interactivity-api/directives/wp-bind.php @@ -0,0 +1,32 @@ +is_tag_closer() ) { + return; + } + + $prefixed_attributes = $tags->get_attribute_names_with_prefix( 'data-wp-bind--' ); + + foreach ( $prefixed_attributes as $attr ) { + list( , $bound_attr ) = WP_Directive_Processor::parse_attribute_name( $attr ); + if ( empty( $bound_attr ) ) { + continue; + } + + $expr = $tags->get_attribute( $attr ); + $value = gutenberg_interactivity_evaluate_reference( $expr, $context->get_context() ); + $tags->set_attribute( $bound_attr, $value ); + } +} diff --git a/lib/experimental/interactivity-api/directives/wp-class.php b/lib/experimental/interactivity-api/directives/wp-class.php new file mode 100644 index 00000000000000..741cc75b42c60e --- /dev/null +++ b/lib/experimental/interactivity-api/directives/wp-class.php @@ -0,0 +1,36 @@ +is_tag_closer() ) { + return; + } + + $prefixed_attributes = $tags->get_attribute_names_with_prefix( 'data-wp-class--' ); + + foreach ( $prefixed_attributes as $attr ) { + list( , $class_name ) = WP_Directive_Processor::parse_attribute_name( $attr ); + if ( empty( $class_name ) ) { + continue; + } + + $expr = $tags->get_attribute( $attr ); + $add_class = gutenberg_interactivity_evaluate_reference( $expr, $context->get_context() ); + if ( $add_class ) { + $tags->add_class( $class_name ); + } else { + $tags->remove_class( $class_name ); + } + } +} diff --git a/lib/experimental/interactivity-api/directives/wp-context.php b/lib/experimental/interactivity-api/directives/wp-context.php new file mode 100644 index 00000000000000..5e3c5a140b2b0d --- /dev/null +++ b/lib/experimental/interactivity-api/directives/wp-context.php @@ -0,0 +1,33 @@ +is_tag_closer() ) { + $context->rewind_context(); + return; + } + + $value = $tags->get_attribute( 'data-wp-context' ); + if ( null === $value ) { + // No data-wp-context directive. + return; + } + + $new_context = json_decode( $value, true ); + if ( null === $new_context ) { + // Invalid JSON defined in the directive. + return; + } + + $context->set_context( $new_context ); +} diff --git a/lib/experimental/interactivity-api/directives/wp-style.php b/lib/experimental/interactivity-api/directives/wp-style.php new file mode 100644 index 00000000000000..9c37f9082c2c0b --- /dev/null +++ b/lib/experimental/interactivity-api/directives/wp-style.php @@ -0,0 +1,72 @@ +is_tag_closer() ) { + return; + } + + $prefixed_attributes = $tags->get_attribute_names_with_prefix( 'data-wp-style--' ); + + foreach ( $prefixed_attributes as $attr ) { + list( , $style_name ) = WP_Directive_Processor::parse_attribute_name( $attr ); + if ( empty( $style_name ) ) { + continue; + } + + $expr = $tags->get_attribute( $attr ); + $style_value = gutenberg_interactivity_evaluate_reference( $expr, $context->get_context() ); + if ( $style_value ) { + $style_attr = $tags->get_attribute( 'style' ); + $style_attr = gutenberg_interactivity_set_style( $style_attr, $style_name, $style_value ); + $tags->set_attribute( 'style', $style_attr ); + } else { + // TODO: Do we want to unset styles if they're null? + } + } +} + +/** + * Set style. + * + * @param string $style Existing style to amend. + * @param string $name Style property name. + * @param string $value Style property value. + * @return string Amended styles. + */ +function gutenberg_interactivity_set_style( $style, $name, $value ) { + $style_assignments = explode( ';', $style ); + $modified = false; + foreach ( $style_assignments as $style_assignment ) { + list( $style_name ) = explode( ':', $style_assignment ); + if ( trim( $style_name ) === $name ) { + // TODO: Retain surrounding whitespace from $style_value, if any. + $style_assignment = $style_name . ': ' . $value; + $modified = true; + break; + } + } + + if ( ! $modified ) { + $new_style_assignment = $name . ': ' . $value; + // If the last element is empty or whitespace-only, we insert + // the new "key: value" pair before it. + if ( empty( trim( end( $style_assignments ) ) ) ) { + array_splice( $style_assignments, - 1, 0, $new_style_assignment ); + } else { + array_push( $style_assignments, $new_style_assignment ); + } + } + return implode( ';', $style_assignments ); +} diff --git a/lib/experimental/interactivity-api/directives/wp-text.php b/lib/experimental/interactivity-api/directives/wp-text.php new file mode 100644 index 00000000000000..b0cfc98a74e702 --- /dev/null +++ b/lib/experimental/interactivity-api/directives/wp-text.php @@ -0,0 +1,27 @@ +is_tag_closer() ) { + return; + } + + $value = $tags->get_attribute( 'data-wp-text' ); + if ( null === $value ) { + return; + } + + $text = gutenberg_interactivity_evaluate_reference( $value, $context->get_context() ); + $tags->set_inner_html( esc_html( $text ) ); +} diff --git a/lib/experimental/interactivity-api/script-loader.php b/lib/experimental/interactivity-api/script-loader.php deleted file mode 100644 index 63453713dd18cf..00000000000000 --- a/lib/experimental/interactivity-api/script-loader.php +++ /dev/null @@ -1,56 +0,0 @@ -next_tag( array( 'tag' => 'script' ) ); - $p->set_attribute( 'defer', true ); - return $p->get_updated_html(); - } - return $tag; -} -add_filter( 'script_loader_tag', 'gutenberg_interactivity_scripts_add_defer_attribute', 10, 2 ); diff --git a/lib/experimental/interactivity-api/scripts.php b/lib/experimental/interactivity-api/scripts.php new file mode 100644 index 00000000000000..e95bf518c75f73 --- /dev/null +++ b/lib/experimental/interactivity-api/scripts.php @@ -0,0 +1,28 @@ +get_all_registered(); + foreach ( array_values( $registered_blocks ) as $block ) { + if ( isset( $block->supports['interactivity'] ) && $block->supports['interactivity'] ) { + foreach ( $block->view_script_handles as $handle ) { + wp_script_add_data( $handle, 'group', 1 ); + } + } + } +} +add_action( 'wp_enqueue_scripts', 'gutenberg_interactivity_move_interactive_scripts_to_the_footer', 11 ); diff --git a/lib/experimental/interactivity-api/store.php b/lib/experimental/interactivity-api/store.php new file mode 100644 index 00000000000000..88c4b2ebd1038a --- /dev/null +++ b/lib/experimental/interactivity-api/store.php @@ -0,0 +1,26 @@ +get_updated_html(); }; - - /** - * Replaces view script for the Navigation block with version using Interactivity API. - * - * @param array $metadata Block metadata as read in via block.json. - * - * @return array Filtered block type metadata. - */ - function gutenberg_block_core_navigation_update_interactive_view_script( $metadata ) { - if ( 'core/navigation' === $metadata['name'] ) { - $metadata['viewScript'] = array( 'file:./interactivity.min.js' ); - } - return $metadata; - } - add_filter( 'block_type_metadata', 'gutenberg_block_core_navigation_update_interactive_view_script', 10, 1 ); } diff --git a/packages/block-library/src/navigation/interactivity.js b/packages/block-library/src/navigation/interactivity.js deleted file mode 100644 index 210e82a3786670..00000000000000 --- a/packages/block-library/src/navigation/interactivity.js +++ /dev/null @@ -1,169 +0,0 @@ -/** - * Internal dependencies - */ -import { store } from '../utils/interactivity'; - -const focusableSelectors = [ - 'a[href]', - 'area[href]', - 'input:not([disabled]):not([type="hidden"]):not([aria-hidden])', - 'select:not([disabled]):not([aria-hidden])', - 'textarea:not([disabled]):not([aria-hidden])', - 'button:not([disabled]):not([aria-hidden])', - 'iframe', - 'object', - 'embed', - '[contenteditable]', - '[tabindex]:not([tabindex^="-"])', -]; - -const openMenu = ( { context, ref }, menuOpenedOn ) => { - context.core.navigation.isMenuOpen[ menuOpenedOn ] = true; - context.core.navigation.previousFocus = ref; - if ( context.core.navigation.overlay ) { - // Add a `has-modal-open` class to the root. - document.documentElement.classList.add( 'has-modal-open' ); - } -}; - -const closeMenu = ( { context, selectors }, menuClosedOn ) => { - context.core.navigation.isMenuOpen[ menuClosedOn ] = false; - // Check if the menu is still open or not. - if ( ! selectors.core.navigation.isMenuOpen( { context } ) ) { - if ( - context.core.navigation.modal.contains( - window.document.activeElement - ) - ) { - context.core.navigation.previousFocus.focus(); - } - context.core.navigation.modal = null; - context.core.navigation.previousFocus = null; - if ( context.core.navigation.overlay ) { - document.documentElement.classList.remove( 'has-modal-open' ); - } - } -}; - -store( { - effects: { - core: { - navigation: { - initMenu: ( { context, selectors, ref } ) => { - if ( selectors.core.navigation.isMenuOpen( { context } ) ) { - const focusableElements = - ref.querySelectorAll( focusableSelectors ); - context.core.navigation.modal = ref; - context.core.navigation.firstFocusableElement = - focusableElements[ 0 ]; - context.core.navigation.lastFocusableElement = - focusableElements[ focusableElements.length - 1 ]; - } - }, - focusFirstElement: ( { context, selectors, ref } ) => { - if ( selectors.core.navigation.isMenuOpen( { context } ) ) { - ref.querySelector( - '.wp-block-navigation-item > *:first-child' - ).focus(); - } - }, - }, - }, - }, - selectors: { - core: { - navigation: { - roleAttribute: ( { context, selectors } ) => - context.core.navigation.overlay && - selectors.core.navigation.isMenuOpen( { context } ) - ? 'dialog' - : '', - isMenuOpen: ( { context } ) => - // The menu is opened if either `click` or `hover` is true. - Object.values( context.core.navigation.isMenuOpen ).filter( - Boolean - ).length > 0, - }, - }, - }, - actions: { - core: { - navigation: { - openMenuOnHover( args ) { - openMenu( args, 'hover' ); - }, - closeMenuOnHover( args ) { - closeMenu( args, 'hover' ); - }, - openMenuOnClick( args ) { - openMenu( args, 'click' ); - }, - closeMenuOnClick( args ) { - closeMenu( args, 'click' ); - }, - toggleMenuOnClick: ( args ) => { - const { context } = args; - if ( context.core.navigation.isMenuOpen.click ) { - closeMenu( args, 'click' ); - } else { - openMenu( args, 'click' ); - } - }, - handleMenuKeydown: ( args ) => { - const { context, event } = args; - if ( context.core.navigation.isMenuOpen.click ) { - // If Escape close the menu - if ( - event?.key === 'Escape' || - event?.keyCode === 27 - ) { - closeMenu( args, 'click' ); - return; - } - - // Trap focus if it is an overlay (main menu) - if ( - context.core.navigation.overlay && - ( event.key === 'Tab' || event.keyCode === 9 ) - ) { - // If shift + tab it change the direction - if ( - event.shiftKey && - window.document.activeElement === - context.core.navigation - .firstFocusableElement - ) { - event.preventDefault(); - context.core.navigation.lastFocusableElement.focus(); - } else if ( - ! event.shiftKey && - window.document.activeElement === - context.core.navigation.lastFocusableElement - ) { - event.preventDefault(); - context.core.navigation.firstFocusableElement.focus(); - } - } - } - }, - handleMenuFocusout: ( args ) => { - const { context, event } = args; - // If focus is outside modal, and in the document, close menu - // event.target === The element losing focus - // event.relatedTarget === The element receiving focus (if any) - // When focusout is outsite the document, - // `window.document.activeElement` doesn't change - if ( - context.core.navigation.isMenuOpen.click && - ! context.core.navigation.modal.contains( - event.relatedTarget - ) && - event.target !== window.document.activeElement - ) { - closeMenu( args, 'click' ); - } - }, - }, - }, - }, -} ); diff --git a/packages/block-library/src/navigation/view-modal.js b/packages/block-library/src/navigation/view-modal.js deleted file mode 100644 index 9477d262816d93..00000000000000 --- a/packages/block-library/src/navigation/view-modal.js +++ /dev/null @@ -1,78 +0,0 @@ -/** - * External dependencies - */ -import MicroModal from 'micromodal'; - -// Responsive navigation toggle. -function navigationToggleModal( modal ) { - const dialogContainer = modal.querySelector( - `.wp-block-navigation__responsive-dialog` - ); - - const isHidden = 'true' === modal.getAttribute( 'aria-hidden' ); - - modal.classList.toggle( 'has-modal-open', ! isHidden ); - dialogContainer.toggleAttribute( 'aria-modal', ! isHidden ); - - if ( isHidden ) { - dialogContainer.removeAttribute( 'role' ); - dialogContainer.removeAttribute( 'aria-modal' ); - } else { - dialogContainer.setAttribute( 'role', 'dialog' ); - dialogContainer.setAttribute( 'aria-modal', 'true' ); - } - - // Add a class to indicate the modal is open. - const htmlElement = document.documentElement; - htmlElement.classList.toggle( 'has-modal-open' ); -} - -function isLinkToAnchorOnCurrentPage( node ) { - return ( - node.hash && - node.protocol === window.location.protocol && - node.host === window.location.host && - node.pathname === window.location.pathname && - node.search === window.location.search - ); -} - -window.addEventListener( 'load', () => { - MicroModal.init( { - onShow: navigationToggleModal, - onClose: navigationToggleModal, - openClass: 'is-menu-open', - } ); - - // Close modal automatically on clicking anchor links inside modal. - const navigationLinks = document.querySelectorAll( - '.wp-block-navigation-item__content' - ); - - navigationLinks.forEach( function ( link ) { - // Ignore non-anchor links and anchor links which open on a new tab. - if ( - ! isLinkToAnchorOnCurrentPage( link ) || - link.attributes?.target === '_blank' - ) { - return; - } - - // Find the specific parent modal for this link - // since .close() won't work without an ID if there are - // multiple navigation menus in a post/page. - const modal = link.closest( - '.wp-block-navigation__responsive-container' - ); - const modalId = modal?.getAttribute( 'id' ); - - link.addEventListener( 'click', () => { - // check if modal exists and is open before trying to close it - // otherwise Micromodal will toggle the `has-modal-open` class - // on the html tag which prevents scrolling - if ( modalId && modal.classList.contains( 'has-modal-open' ) ) { - MicroModal.close( modalId ); - } - } ); - } ); -} ); diff --git a/packages/block-library/src/navigation/view.js b/packages/block-library/src/navigation/view.js index 19805a44ae4ae2..8fcc5527c6042c 100644 --- a/packages/block-library/src/navigation/view.js +++ b/packages/block-library/src/navigation/view.js @@ -1,74 +1,169 @@ -// Open on click functionality. -function closeSubmenus( element ) { - element - .querySelectorAll( '[aria-expanded="true"]' ) - .forEach( function ( toggle ) { - toggle.setAttribute( 'aria-expanded', 'false' ); - } ); -} +/** + * WordPress dependencies + */ +import { store } from '@wordpress/interactivity'; -function toggleSubmenuOnClick( event ) { - const buttonToggle = event.target.closest( '[aria-expanded]' ); - const isSubmenuOpen = buttonToggle.getAttribute( 'aria-expanded' ); +const focusableSelectors = [ + 'a[href]', + 'area[href]', + 'input:not([disabled]):not([type="hidden"]):not([aria-hidden])', + 'select:not([disabled]):not([aria-hidden])', + 'textarea:not([disabled]):not([aria-hidden])', + 'button:not([disabled]):not([aria-hidden])', + 'iframe', + 'object', + 'embed', + '[contenteditable]', + '[tabindex]:not([tabindex^="-"])', +]; - if ( isSubmenuOpen === 'true' ) { - closeSubmenus( buttonToggle.closest( '.wp-block-navigation-item' ) ); - } else { - // Close all sibling submenus. - const parentElement = buttonToggle.closest( - '.wp-block-navigation-item' - ); - const navigationParent = buttonToggle.closest( - '.wp-block-navigation__submenu-container, .wp-block-navigation__container, .wp-block-page-list' - ); - navigationParent - .querySelectorAll( '.wp-block-navigation-item' ) - .forEach( function ( child ) { - if ( child !== parentElement ) { - closeSubmenus( child ); - } - } ); - // Open submenu. - buttonToggle.setAttribute( 'aria-expanded', 'true' ); +const openMenu = ( { context, ref }, menuOpenedOn ) => { + context.core.navigation.isMenuOpen[ menuOpenedOn ] = true; + context.core.navigation.previousFocus = ref; + if ( context.core.navigation.overlay ) { + // Add a `has-modal-open` class to the root. + document.documentElement.classList.add( 'has-modal-open' ); } -} +}; -// Necessary for some themes such as TT1 Blocks, where -// scripts could be loaded before the body. -window.addEventListener( 'load', () => { - const submenuButtons = document.querySelectorAll( - '.wp-block-navigation-submenu__toggle' - ); +const closeMenu = ( { context, selectors }, menuClosedOn ) => { + context.core.navigation.isMenuOpen[ menuClosedOn ] = false; + // Check if the menu is still open or not. + if ( ! selectors.core.navigation.isMenuOpen( { context } ) ) { + if ( + context.core.navigation.modal.contains( + window.document.activeElement + ) + ) { + context.core.navigation.previousFocus.focus(); + } + context.core.navigation.modal = null; + context.core.navigation.previousFocus = null; + if ( context.core.navigation.overlay ) { + document.documentElement.classList.remove( 'has-modal-open' ); + } + } +}; - submenuButtons.forEach( function ( button ) { - button.addEventListener( 'click', toggleSubmenuOnClick ); - } ); +store( { + effects: { + core: { + navigation: { + initMenu: ( { context, selectors, ref } ) => { + if ( selectors.core.navigation.isMenuOpen( { context } ) ) { + const focusableElements = + ref.querySelectorAll( focusableSelectors ); + context.core.navigation.modal = ref; + context.core.navigation.firstFocusableElement = + focusableElements[ 0 ]; + context.core.navigation.lastFocusableElement = + focusableElements[ focusableElements.length - 1 ]; + } + }, + focusFirstElement: ( { context, selectors, ref } ) => { + if ( selectors.core.navigation.isMenuOpen( { context } ) ) { + ref.querySelector( + '.wp-block-navigation-item > *:first-child' + ).focus(); + } + }, + }, + }, + }, + selectors: { + core: { + navigation: { + roleAttribute: ( { context, selectors } ) => + context.core.navigation.overlay && + selectors.core.navigation.isMenuOpen( { context } ) + ? 'dialog' + : '', + isMenuOpen: ( { context } ) => + // The menu is opened if either `click` or `hover` is true. + Object.values( context.core.navigation.isMenuOpen ).filter( + Boolean + ).length > 0, + }, + }, + }, + actions: { + core: { + navigation: { + openMenuOnHover( args ) { + openMenu( args, 'hover' ); + }, + closeMenuOnHover( args ) { + closeMenu( args, 'hover' ); + }, + openMenuOnClick( args ) { + openMenu( args, 'click' ); + }, + closeMenuOnClick( args ) { + closeMenu( args, 'click' ); + }, + toggleMenuOnClick: ( args ) => { + const { context } = args; + if ( context.core.navigation.isMenuOpen.click ) { + closeMenu( args, 'click' ); + } else { + openMenu( args, 'click' ); + } + }, + handleMenuKeydown: ( args ) => { + const { context, event } = args; + if ( context.core.navigation.isMenuOpen.click ) { + // If Escape close the menu + if ( + event?.key === 'Escape' || + event?.keyCode === 27 + ) { + closeMenu( args, 'click' ); + return; + } - // Close on click outside. - document.addEventListener( 'click', function ( event ) { - const navigationBlocks = document.querySelectorAll( - '.wp-block-navigation' - ); - navigationBlocks.forEach( function ( block ) { - if ( ! block.contains( event.target ) ) { - closeSubmenus( block ); - } - } ); - } ); - // Close on focus outside or escape key. - document.addEventListener( 'keyup', function ( event ) { - const submenuBlocks = document.querySelectorAll( - '.wp-block-navigation-item.has-child' - ); - submenuBlocks.forEach( function ( block ) { - if ( ! block.contains( event.target ) ) { - closeSubmenus( block ); - } else if ( event.key === 'Escape' ) { - const toggle = block.querySelector( '[aria-expanded="true"]' ); - closeSubmenus( block ); - // Focus the submenu trigger so focus does not get trapped in the closed submenu. - toggle?.focus(); - } - } ); - } ); + // Trap focus if it is an overlay (main menu) + if ( + context.core.navigation.overlay && + ( event.key === 'Tab' || event.keyCode === 9 ) + ) { + // If shift + tab it change the direction + if ( + event.shiftKey && + window.document.activeElement === + context.core.navigation + .firstFocusableElement + ) { + event.preventDefault(); + context.core.navigation.lastFocusableElement.focus(); + } else if ( + ! event.shiftKey && + window.document.activeElement === + context.core.navigation.lastFocusableElement + ) { + event.preventDefault(); + context.core.navigation.firstFocusableElement.focus(); + } + } + } + }, + handleMenuFocusout: ( args ) => { + const { context, event } = args; + // If focus is outside modal, and in the document, close menu + // event.target === The element losing focus + // event.relatedTarget === The element receiving focus (if any) + // When focusout is outsite the document, + // `window.document.activeElement` doesn't change + if ( + context.core.navigation.isMenuOpen.click && + ! context.core.navigation.modal.contains( + event.relatedTarget + ) && + event.target !== window.document.activeElement + ) { + closeMenu( args, 'click' ); + } + }, + }, + }, + }, } ); diff --git a/packages/dependency-extraction-webpack-plugin/lib/index.js b/packages/dependency-extraction-webpack-plugin/lib/index.js index 3da2286ddbd57d..581274c3684f93 100644 --- a/packages/dependency-extraction-webpack-plugin/lib/index.js +++ b/packages/dependency-extraction-webpack-plugin/lib/index.js @@ -27,7 +27,6 @@ class DependencyExtractionWebpackPlugin { combinedOutputFile: null, externalizedReport: false, injectPolyfill: false, - __experimentalInjectInteractivityRuntime: false, outputFormat: 'php', outputFilename: null, useDefaults: true, @@ -143,7 +142,6 @@ class DependencyExtractionWebpackPlugin { combinedOutputFile, externalizedReport, injectPolyfill, - __experimentalInjectInteractivityRuntime, outputFormat, outputFilename, } = this.options; @@ -186,14 +184,6 @@ class DependencyExtractionWebpackPlugin { if ( injectPolyfill ) { chunkDeps.add( 'wp-polyfill' ); } - // Temporary fix for Interactivity API until it gets moved to its package. - if ( __experimentalInjectInteractivityRuntime ) { - if ( ! chunkJSFile.startsWith( './interactivity/' ) ) { - chunkDeps.add( 'wp-interactivity-runtime' ); - } else if ( './interactivity/runtime.min.js' === chunkJSFile ) { - chunkDeps.add( 'wp-interactivity-vendors' ); - } - } const processModule = ( { userRequest } ) => { if ( this.externalizedDeps.has( userRequest ) ) { diff --git a/packages/e2e-test-utils-playwright/src/request-utils/posts.ts b/packages/e2e-test-utils-playwright/src/request-utils/posts.ts index e02dc11d3842a0..5e32c0c877555c 100644 --- a/packages/e2e-test-utils-playwright/src/request-utils/posts.ts +++ b/packages/e2e-test-utils-playwright/src/request-utils/posts.ts @@ -7,6 +7,7 @@ export interface Post { id: number; content: string; status: 'publish' | 'future' | 'draft' | 'pending' | 'private'; + link: string; } export interface CreatePostPayload { diff --git a/packages/e2e-tests/plugins/interactive-blocks.php b/packages/e2e-tests/plugins/interactive-blocks.php new file mode 100644 index 00000000000000..8a44e5a0efd4ac --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks.php @@ -0,0 +1,48 @@ + +
+ + + + + + + + + + + + + + +

+ Some Text +

+
diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-bind/view.js b/packages/e2e-tests/plugins/interactive-blocks/directive-bind/view.js new file mode 100644 index 00000000000000..0cd590f184cc85 --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-bind/view.js @@ -0,0 +1,23 @@ +( ( { wp } ) => { + const { store } = wp.interactivity; + + store( { + state: { + url: '/some-url', + checked: true, + show: false, + width: 1, + }, + foo: { + bar: 1, + }, + actions: { + toggle: ( { state, foo } ) => { + state.url = '/some-other-url'; + state.checked = ! state.checked; + state.show = ! state.show; + state.width += foo.bar; + }, + }, + } ); +} )( window ); diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-class/block.json b/packages/e2e-tests/plugins/interactive-blocks/directive-class/block.json new file mode 100644 index 00000000000000..af2764db986919 --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-class/block.json @@ -0,0 +1,14 @@ +{ + "apiVersion": 2, + "name": "test/directive-class", + "title": "E2E Interactivity tests - directive class", + "category": "text", + "icon": "heart", + "description": "", + "supports": { + "interactivity": true + }, + "textdomain": "e2e-interactivity", + "viewScript": "directive-class-view", + "render": "file:./render.php" +} diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-class/render.php b/packages/e2e-tests/plugins/interactive-blocks/directive-class/render.php new file mode 100644 index 00000000000000..19da052dc1148c --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-class/render.php @@ -0,0 +1,75 @@ + +
+ + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+
+ +
+
diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-class/view.js b/packages/e2e-tests/plugins/interactive-blocks/directive-class/view.js new file mode 100644 index 00000000000000..bb06cf38412811 --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-class/view.js @@ -0,0 +1,21 @@ +( ( { wp } ) => { + const { store } = wp.interactivity; + + store( { + state: { + trueValue: true, + falseValue: false, + }, + actions: { + toggleTrueValue: ( { state } ) => { + state.trueValue = ! state.trueValue; + }, + toggleFalseValue: ( { state } ) => { + state.falseValue = ! state.falseValue; + }, + toggleContextFalseValue: ( { context } ) => { + context.falseValue = ! context.falseValue; + }, + }, + } ); +} )( window ); diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-context/block.json b/packages/e2e-tests/plugins/interactive-blocks/directive-context/block.json new file mode 100644 index 00000000000000..1b3c448cc62aac --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-context/block.json @@ -0,0 +1,14 @@ +{ + "apiVersion": 2, + "name": "test/directive-context", + "title": "E2E Interactivity tests - directive context", + "category": "text", + "icon": "heart", + "description": "", + "supports": { + "interactivity": true + }, + "textdomain": "e2e-interactivity", + "viewScript": "directive-context-view", + "render": "file:./render.php" +} diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-context/render.php b/packages/e2e-tests/plugins/interactive-blocks/directive-context/render.php new file mode 100644 index 00000000000000..a9b0402d1b094e --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-context/render.php @@ -0,0 +1,121 @@ + +
+
+
+			
+		
+ + + + +
+
+				
+			
+ + + + + + +
+
+ + +
+
diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-context/view.js b/packages/e2e-tests/plugins/interactive-blocks/directive-context/view.js new file mode 100644 index 00000000000000..46483aaa2ea53d --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-context/view.js @@ -0,0 +1,22 @@ +( ( { wp } ) => { + const { store } = wp.interactivity; + + store( { + derived: { + renderContext: ( { context } ) => { + return JSON.stringify( context, undefined, 2 ); + }, + }, + actions: { + updateContext: ( { context, event } ) => { + const { name, value } = event.target; + const [ key, ...path ] = name.split( '.' ).reverse(); + const obj = path.reduceRight( ( o, k ) => o[ k ], context ); + obj[ key ] = value; + }, + toggleContextText: ( { context } ) => { + context.text = context.text === 'Text 1' ? 'Text 2' : 'Text 1'; + }, + }, + } ); +} )( window ); diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-effect/block.json b/packages/e2e-tests/plugins/interactive-blocks/directive-effect/block.json new file mode 100644 index 00000000000000..b9cb2f782b2e6f --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-effect/block.json @@ -0,0 +1,14 @@ +{ + "apiVersion": 2, + "name": "test/directive-effect", + "title": "E2E Interactivity tests - directive effect", + "category": "text", + "icon": "heart", + "description": "", + "supports": { + "interactivity": true + }, + "textdomain": "e2e-interactivity", + "viewScript": "directive-effect-view", + "render": "file:./render.php" +} diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-effect/render.php b/packages/e2e-tests/plugins/interactive-blocks/directive-effect/render.php new file mode 100644 index 00000000000000..243826aae35046 --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-effect/render.php @@ -0,0 +1,27 @@ + +
+
+ +
+ +
+ +
+ + +
diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-effect/view.js b/packages/e2e-tests/plugins/interactive-blocks/directive-effect/view.js new file mode 100644 index 00000000000000..debb363a9ac0fd --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-effect/view.js @@ -0,0 +1,61 @@ +( ( { wp } ) => { + const { store, directive, useContext, useMemo } = wp.interactivity; + + // Fake `data-wp-fakeshow` directive to test when things are removed from the DOM. + // Replace with `data-wp-show` when it's ready. + directive( + 'fakeshow', + ( { + directives: { + fakeshow: { default: fakeshow }, + }, + element, + evaluate, + context, + } ) => { + const contextValue = useContext( context ); + const children = useMemo( + () => + element.type === 'template' + ? element.props.templateChildren + : element, + [] + ); + if ( ! evaluate( fakeshow, { context: contextValue } ) ) return null; + return children; + } + ); + + store( { + state: { + isOpen: true, + isElementInTheDOM: false, + }, + selectors: { + elementInTheDOM: ( { state } ) => + state.isElementInTheDOM + ? 'element is in the DOM' + : 'element is not in the DOM', + }, + actions: { + toggle( { state } ) { + state.isOpen = ! state.isOpen; + }, + }, + effects: { + elementAddedToTheDOM: ( { state } ) => { + state.isElementInTheDOM = true; + + return () => { + state.isElementInTheDOM = false; + }; + }, + changeFocus: ( { state } ) => { + if ( state.isOpen ) { + document.querySelector( "[data-testid='input']" ).focus(); + } + }, + }, + } ); + +} )( window ); diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-priorities/block.json b/packages/e2e-tests/plugins/interactive-blocks/directive-priorities/block.json new file mode 100644 index 00000000000000..c7361c3d5f121a --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-priorities/block.json @@ -0,0 +1,14 @@ +{ + "apiVersion": 2, + "name": "test/directive-priorities", + "title": "E2E Interactivity tests - directive priorities", + "category": "text", + "icon": "heart", + "description": "", + "supports": { + "interactivity": true + }, + "textdomain": "e2e-interactivity", + "viewScript": "directive-priorities-view", + "render": "file:./render.php" +} diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-priorities/render.php b/packages/e2e-tests/plugins/interactive-blocks/directive-priorities/render.php new file mode 100644 index 00000000000000..a5f0b045ab0d67 --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-priorities/render.php @@ -0,0 +1,20 @@ + +
+

+
+	
+	
+
diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-priorities/view.js b/packages/e2e-tests/plugins/interactive-blocks/directive-priorities/view.js new file mode 100644 index 00000000000000..cedc0c7c1d3ad3 --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-priorities/view.js @@ -0,0 +1,121 @@ +( ( { wp } ) => { + /** + * WordPress dependencies + */ + const { + store, + directive, + deepSignal, + useContext, + useEffect, + createElement: h + } = wp.interactivity; + + /** + * Util to check that render calls happen in order. + * + * @param {string} n Name passed from the directive being executed. + */ + const executionProof = ( n ) => { + const el = document.querySelector( '[data-testid="execution order"]' ); + if ( ! el.textContent ) el.textContent = n; + else el.textContent += `, ${ n }`; + }; + + /** + * Simple context directive, just for testing purposes. It provides a deep + * signal with these two properties: + * - attribute: 'from context' + * - text: 'from context' + */ + directive( + 'test-context', + ( { context: { Provider }, props: { children } } ) => { + executionProof( 'context' ); + const value = deepSignal( { + attribute: 'from context', + text: 'from context', + } ); + return h( Provider, { value }, children ); + }, + { priority: 8 } + ); + + /** + * Simple attribute directive, for testing purposes. It reads the value of + * `attribute` from context and populates `data-attribute` with it. + */ + directive( 'test-attribute', ( { context, evaluate, element } ) => { + executionProof( 'attribute' ); + const contextValue = useContext( context ); + const attributeValue = evaluate( 'context.attribute', { + context: contextValue, + } ); + useEffect( () => { + element.ref.current.setAttribute( + 'data-attribute', + attributeValue, + ); + }, [] ); + element.props[ 'data-attribute' ] = attributeValue; + } ); + + /** + * Simple text directive, for testing purposes. It reads the value of + * `text` from context and populates `children` with it. + */ + directive( + 'test-text', + ( { context, evaluate, element } ) => { + executionProof( 'text' ); + const contextValue = useContext( context ); + const textValue = evaluate( 'context.text', { + context: contextValue, + } ); + element.props.children = + h( 'p', { 'data-testid': 'text' }, textValue ); + }, + { priority: 12 } + ); + + /** + * Children directive, for testing purposes. It adds a wrapper around + * `children`, including two buttons to modify `text` and `attribute` values + * from the received context. + */ + directive( + 'test-children', + ( { context, evaluate, element } ) => { + executionProof( 'children' ); + const contextValue = useContext( context ); + const updateAttribute = () => { + evaluate( + 'actions.updateAttribute', + { context: contextValue } + ); + }; + const updateText = () => { + evaluate( 'actions.updateText', { context: contextValue } ); + }; + element.props.children = h( + 'div', + {}, + element.props.children, + h( 'button', { onClick: updateAttribute }, 'Update attribute' ), + h( 'button', { onClick: updateText }, 'Update text' ) + ); + }, + { priority: 14 } + ); + + store( { + actions: { + updateText( { context } ) { + context.text = 'updated'; + }, + updateAttribute( { context } ) { + context.attribute = 'updated'; + }, + }, + } ); +} )( window ); diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-show/block.json b/packages/e2e-tests/plugins/interactive-blocks/directive-show/block.json new file mode 100644 index 00000000000000..ab6801843a7cae --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-show/block.json @@ -0,0 +1,14 @@ +{ + "apiVersion": 2, + "name": "test/directive-show", + "title": "E2E Interactivity tests - directive show", + "category": "text", + "icon": "heart", + "description": "", + "supports": { + "interactivity": true + }, + "textdomain": "e2e-interactivity", + "viewScript": "directive-show-view", + "render": "file:./render.php" +} diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-show/render.php b/packages/e2e-tests/plugins/interactive-blocks/directive-show/render.php new file mode 100644 index 00000000000000..5818f3b7c10b63 --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-show/render.php @@ -0,0 +1,53 @@ + +
+ + + + +
+

trueValue children

+
+ +
+

falseValue children

+
+ +
+
+ falseValue +
+ +
+
diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-show/view.js b/packages/e2e-tests/plugins/interactive-blocks/directive-show/view.js new file mode 100644 index 00000000000000..9398321b4cfe43 --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-show/view.js @@ -0,0 +1,24 @@ +( ( { wp } ) => { + /** + * WordPress dependencies + */ + const { store } = wp.interactivity; + + store( { + state: { + trueValue: true, + falseValue: false, + }, + actions: { + toggleTrueValue: ( { state } ) => { + state.trueValue = ! state.trueValue; + }, + toggleFalseValue: ( { state } ) => { + state.falseValue = ! state.falseValue; + }, + toggleContextFalseValue: ( { context } ) => { + context.falseValue = ! context.falseValue; + }, + }, + } ); +} )( window ); diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-text/block.json b/packages/e2e-tests/plugins/interactive-blocks/directive-text/block.json new file mode 100644 index 00000000000000..7295849b9912d7 --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-text/block.json @@ -0,0 +1,14 @@ +{ + "apiVersion": 2, + "name": "test/directive-text", + "title": "E2E Interactivity tests - directive text", + "category": "text", + "icon": "heart", + "description": "", + "supports": { + "interactivity": true + }, + "textdomain": "e2e-interactivity", + "viewScript": "directive-text-view", + "render": "file:./render.php" +} diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-text/render.php b/packages/e2e-tests/plugins/interactive-blocks/directive-text/render.php new file mode 100644 index 00000000000000..54ac9c09e7d863 --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-text/render.php @@ -0,0 +1,35 @@ + +
+
+ + +
+ +
+ + +
+
diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-text/view.js b/packages/e2e-tests/plugins/interactive-blocks/directive-text/view.js new file mode 100644 index 00000000000000..49121213f2b04e --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-text/view.js @@ -0,0 +1,17 @@ +( ( { wp } ) => { + const { store } = wp.interactivity; + + store( { + state: { + text: 'Text 1', + }, + actions: { + toggleStateText: ( { state } ) => { + state.text = state.text === 'Text 1' ? 'Text 2' : 'Text 1'; + }, + toggleContextText: ( { context } ) => { + context.text = context.text === 'Text 1' ? 'Text 2' : 'Text 1'; + }, + }, + } ); +} )( window ); diff --git a/packages/e2e-tests/plugins/interactive-blocks/negation-operator/block.json b/packages/e2e-tests/plugins/interactive-blocks/negation-operator/block.json new file mode 100644 index 00000000000000..68da53367ad63a --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/negation-operator/block.json @@ -0,0 +1,14 @@ +{ + "apiVersion": 2, + "name": "test/negation-operator", + "title": "E2E Interactivity tests - negation operator", + "category": "text", + "icon": "heart", + "description": "", + "supports": { + "interactivity": true + }, + "textdomain": "e2e-interactivity", + "viewScript": "negation-operator-view", + "render": "file:./render.php" +} diff --git a/packages/e2e-tests/plugins/interactive-blocks/negation-operator/render.php b/packages/e2e-tests/plugins/interactive-blocks/negation-operator/render.php new file mode 100644 index 00000000000000..e087a5eceb8369 --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/negation-operator/render.php @@ -0,0 +1,26 @@ + +
+ + +
+ +
+
diff --git a/packages/e2e-tests/plugins/interactive-blocks/negation-operator/view.js b/packages/e2e-tests/plugins/interactive-blocks/negation-operator/view.js new file mode 100644 index 00000000000000..64a84269c356e3 --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/negation-operator/view.js @@ -0,0 +1,22 @@ +( ( { wp } ) => { + /** + * WordPress dependencies + */ + const { store } = wp.interactivity; + + store( { + selectors: { + active: ( { state } ) => { + return state.active; + }, + }, + state: { + active: false, + }, + actions: { + toggle: ( { state } ) => { + state.active = ! state.active; + }, + }, + } ); +} )( window ); diff --git a/packages/e2e-tests/plugins/interactive-blocks/store-tag/block.json b/packages/e2e-tests/plugins/interactive-blocks/store-tag/block.json new file mode 100644 index 00000000000000..4611288de796c9 --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/store-tag/block.json @@ -0,0 +1,14 @@ +{ + "apiVersion": 2, + "name": "test/store-tag", + "title": "E2E Interactivity tests - store tag", + "category": "text", + "icon": "heart", + "description": "", + "supports": { + "interactivity": true + }, + "textdomain": "e2e-interactivity", + "viewScript": "store-tag-view", + "render": "file:./render.php" +} diff --git a/packages/e2e-tests/plugins/interactive-blocks/store-tag/render.php b/packages/e2e-tests/plugins/interactive-blocks/store-tag/render.php new file mode 100644 index 00000000000000..9bc8126720b9b9 --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/store-tag/render.php @@ -0,0 +1,64 @@ + +
+
+ Counter: + +
+ Double: + +
+ + 0 + clicks +
+
+ + $test_store_tag_json + +HTML; +} diff --git a/packages/e2e-tests/plugins/interactive-blocks/store-tag/view.js b/packages/e2e-tests/plugins/interactive-blocks/store-tag/view.js new file mode 100644 index 00000000000000..140cab6463137f --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/store-tag/view.js @@ -0,0 +1,24 @@ +( ( { wp } ) => { + /** + * WordPress dependencies + */ + const { store } = wp.interactivity; + + store( { + state: { + counter: { + // `value` is defined in the server. + double: ( { state } ) => state.counter.value * 2, + clicks: 0, + }, + }, + actions: { + counter: { + increment: ( { state } ) => { + state.counter.value += 1; + state.counter.clicks += 1; + }, + }, + }, + } ); +} )( window ); diff --git a/packages/e2e-tests/plugins/interactive-blocks/tovdom-islands/block.json b/packages/e2e-tests/plugins/interactive-blocks/tovdom-islands/block.json new file mode 100644 index 00000000000000..fb852acefb9116 --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/tovdom-islands/block.json @@ -0,0 +1,14 @@ +{ + "apiVersion": 2, + "name": "test/tovdom-islands", + "title": "E2E Interactivity tests - tovdom islands", + "category": "text", + "icon": "heart", + "description": "", + "supports": { + "interactivity": true + }, + "textdomain": "e2e-interactivity", + "viewScript": "tovdom-islands-view", + "render": "file:./render.php" +} diff --git a/packages/e2e-tests/plugins/interactive-blocks/tovdom-islands/render.php b/packages/e2e-tests/plugins/interactive-blocks/tovdom-islands/render.php new file mode 100644 index 00000000000000..aa0f2e68d3b7cf --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/tovdom-islands/render.php @@ -0,0 +1,66 @@ + +
+
+ + This should be shown because it is inside an island. + +
+ +
+
+ + This should not be shown because it is inside an island. + +
+
+ +
+
+
+ + This should be shown because it is inside an inner + block of an isolated island. + +
+
+
+ +
+
+
+ + This should not have two template wrappers because + that means we hydrated twice. + +
+
+
+ +
+
+
+
+ + This should not be shown because even though it + is inside an inner block of an isolated island, + it's inside an new island. + +
+
+
+
+
diff --git a/packages/e2e-tests/plugins/interactive-blocks/tovdom-islands/view.js b/packages/e2e-tests/plugins/interactive-blocks/tovdom-islands/view.js new file mode 100644 index 00000000000000..3ccb09203e6bb6 --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/tovdom-islands/view.js @@ -0,0 +1,9 @@ +( ( { wp } ) => { + const { store } = wp.interactivity; + + store( { + state: { + falseValue: false, + }, + } ); +} )( window ); diff --git a/packages/e2e-tests/plugins/interactive-blocks/tovdom/block.json b/packages/e2e-tests/plugins/interactive-blocks/tovdom/block.json new file mode 100644 index 00000000000000..b685919e164821 --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/tovdom/block.json @@ -0,0 +1,14 @@ +{ + "apiVersion": 2, + "name": "test/tovdom", + "title": "E2E Interactivity tests - tovdom", + "category": "text", + "icon": "heart", + "description": "", + "supports": { + "interactivity": true + }, + "textdomain": "e2e-interactivity", + "viewScript": "tovdom-view", + "render": "file:./render.php" +} diff --git a/packages/e2e-tests/plugins/interactive-blocks/tovdom/cdata.js b/packages/e2e-tests/plugins/interactive-blocks/tovdom/cdata.js new file mode 100644 index 00000000000000..506e899e42850c --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/tovdom/cdata.js @@ -0,0 +1,15 @@ +const cdata = ` +
+ +
+ +
+
+ `; + +const cdataElement = new DOMParser() + .parseFromString( cdata, 'text/xml' ) + .querySelector( 'div' ); +document + .getElementById( 'replace-with-cdata' ) + .replaceWith( cdataElement ); diff --git a/packages/e2e-tests/plugins/interactive-blocks/tovdom/processing-instructions.js b/packages/e2e-tests/plugins/interactive-blocks/tovdom/processing-instructions.js new file mode 100644 index 00000000000000..b65095ef6cde4f --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/tovdom/processing-instructions.js @@ -0,0 +1,16 @@ +const processingInstructions = ` +
+ +
+ Processing instructions inner node + +
+
+ `; + +const processingInstructionsElement = new DOMParser() + .parseFromString( processingInstructions, 'text/xml' ) + .querySelector( 'div' ); +document + .getElementById( 'replace-with-processing-instructions' ) + .replaceWith( processingInstructionsElement ); diff --git a/packages/e2e-tests/plugins/interactive-blocks/tovdom/render.php b/packages/e2e-tests/plugins/interactive-blocks/tovdom/render.php new file mode 100644 index 00000000000000..952a4f6c0a455d --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/tovdom/render.php @@ -0,0 +1,33 @@ + + +
+
+ +
+ Comments inner node + +
+
+ +
+
+
+ + + +
+
+
+ + +
diff --git a/packages/e2e-tests/plugins/interactive-blocks/tovdom/view.js b/packages/e2e-tests/plugins/interactive-blocks/tovdom/view.js new file mode 100644 index 00000000000000..734ccbd801bb1e --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/tovdom/view.js @@ -0,0 +1,5 @@ +( ( { wp } ) => { + const { store } = wp.interactivity; + + store( {} ); +} )( window ); diff --git a/packages/interactivity/.npmrc b/packages/interactivity/.npmrc new file mode 100644 index 00000000000000..43c97e719a5a82 --- /dev/null +++ b/packages/interactivity/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/packages/interactivity/README.md b/packages/interactivity/README.md new file mode 100644 index 00000000000000..508cc06588f752 --- /dev/null +++ b/packages/interactivity/README.md @@ -0,0 +1 @@ +# Interactivity diff --git a/packages/interactivity/package.json b/packages/interactivity/package.json new file mode 100644 index 00000000000000..15f6c09fe302f6 --- /dev/null +++ b/packages/interactivity/package.json @@ -0,0 +1,35 @@ +{ + "name": "@wordpress/interactivity", + "version": "1.0.0", + "description": "Package that provides a standard and simple way to handle the frontend interactivity of Gutenberg blocks.", + "author": "The WordPress Contributors", + "license": "GPL-2.0-or-later", + "keywords": [ + "wordpress", + "gutenberg", + "interactivity" + ], + "homepage": "https://github.com/WordPress/gutenberg/tree/HEAD/packages/interactivity/README.md", + "repository": { + "type": "git", + "url": "https://github.com/WordPress/gutenberg.git", + "directory": "packages/interactivity" + }, + "bugs": { + "url": "https://github.com/WordPress/gutenberg/labels/%5BFeature%5D%20Interactivity%20API" + }, + "engines": { + "node": ">=12" + }, + "main": "build/index.js", + "module": "build-module/index.js", + "react-native": "src/index", + "dependencies": { + "@preact/signals": "^1.1.3", + "deepsignal": "^1.3.0", + "preact": "^10.13.2" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/block-library/src/utils/interactivity/constants.js b/packages/interactivity/src/constants.js similarity index 100% rename from packages/block-library/src/utils/interactivity/constants.js rename to packages/interactivity/src/constants.js diff --git a/packages/block-library/src/utils/interactivity/directives.js b/packages/interactivity/src/directives.js similarity index 88% rename from packages/block-library/src/utils/interactivity/directives.js rename to packages/interactivity/src/directives.js index ba1532c5595131..ca87f7655dba68 100644 --- a/packages/block-library/src/utils/interactivity/directives.js +++ b/packages/interactivity/src/directives.js @@ -3,14 +3,11 @@ */ import { useContext, useMemo, useEffect } from 'preact/hooks'; import { deepSignal, peek } from 'deepsignal'; -/** - * Internal dependencies - */ -import { createPortal } from './portals.js'; /** * Internal dependencies */ +import { createPortal } from './portals'; import { useSignalEffect } from './utils'; import { directive } from './hooks'; @@ -178,6 +175,26 @@ export default () => { } ); + // data-wp-show + directive( + 'show', + ( { + directives: { + show: { default: show }, + }, + element, + evaluate, + context, + } ) => { + const contextValue = useContext( context ); + + if ( ! evaluate( show, { context: contextValue } ) ) + element.props.children = ( + + ); + } + ); + // data-wp-ignore directive( 'ignore', @@ -197,4 +214,22 @@ export default () => { ); } ); + + // data-wp-text + directive( + 'text', + ( { + directives: { + text: { default: text }, + }, + element, + evaluate, + context, + } ) => { + const contextValue = useContext( context ); + element.props.children = evaluate( text, { + context: contextValue, + } ); + } + ); }; diff --git a/packages/block-library/src/utils/interactivity/hooks.js b/packages/interactivity/src/hooks.js similarity index 100% rename from packages/block-library/src/utils/interactivity/hooks.js rename to packages/interactivity/src/hooks.js diff --git a/packages/block-library/src/utils/interactivity/hydration.js b/packages/interactivity/src/hydration.js similarity index 100% rename from packages/block-library/src/utils/interactivity/hydration.js rename to packages/interactivity/src/hydration.js diff --git a/packages/block-library/src/utils/interactivity/index.js b/packages/interactivity/src/index.js similarity index 61% rename from packages/block-library/src/utils/interactivity/index.js rename to packages/interactivity/src/index.js index 71b6a2b790704d..9d1b6b695b66b7 100644 --- a/packages/block-library/src/utils/interactivity/index.js +++ b/packages/interactivity/src/index.js @@ -4,6 +4,10 @@ import registerDirectives from './directives'; import { init } from './hydration'; export { store } from './store'; +export { directive } from './hooks'; +export { h as createElement } from 'preact'; +export { useEffect, useContext, useMemo } from 'preact/hooks'; +export { deepSignal } from 'deepsignal'; /** * Initialize the Interactivity API. diff --git a/packages/block-library/src/utils/interactivity/portals.js b/packages/interactivity/src/portals.js similarity index 100% rename from packages/block-library/src/utils/interactivity/portals.js rename to packages/interactivity/src/portals.js diff --git a/packages/block-library/src/utils/interactivity/store.js b/packages/interactivity/src/store.js similarity index 92% rename from packages/block-library/src/utils/interactivity/store.js rename to packages/interactivity/src/store.js index d11af901352017..207fd2d58cfc95 100644 --- a/packages/block-library/src/utils/interactivity/store.js +++ b/packages/interactivity/src/store.js @@ -20,9 +20,8 @@ const deepMerge = ( target, source ) => { }; const getSerializedState = () => { - // TODO: change the store tag ID for a better one. const storeTag = document.querySelector( - `script[type="application/json"]#store` + `script[type="application/json"]#wp-interactivity-store-data` ); if ( ! storeTag ) return {}; try { diff --git a/packages/block-library/src/utils/interactivity/utils.js b/packages/interactivity/src/utils.js similarity index 100% rename from packages/block-library/src/utils/interactivity/utils.js rename to packages/interactivity/src/utils.js diff --git a/packages/block-library/src/utils/interactivity/vdom.js b/packages/interactivity/src/vdom.js similarity index 100% rename from packages/block-library/src/utils/interactivity/vdom.js rename to packages/interactivity/src/vdom.js diff --git a/phpunit/experimental/interactivity-api/class-wp-directive-processor-test.php b/phpunit/experimental/interactivity-api/class-wp-directive-processor-test.php new file mode 100644 index 00000000000000..2b01cb6251c210 --- /dev/null +++ b/phpunit/experimental/interactivity-api/class-wp-directive-processor-test.php @@ -0,0 +1,132 @@ +outside
inside
'; + + public function test_next_balanced_closer_stays_on_void_tag() { + $tags = new WP_Directive_Processor( self::HTML ); + + $tags->next_tag( 'img' ); + $result = $tags->next_balanced_closer(); + $this->assertSame( 'IMG', $tags->get_tag() ); + $this->assertFalse( $result ); + } + + public function test_next_balanced_closer_proceeds_to_correct_tag() { + $tags = new WP_Directive_Processor( self::HTML ); + + $tags->next_tag( 'section' ); + $tags->next_balanced_closer(); + $this->assertSame( 'SECTION', $tags->get_tag() ); + $this->assertTrue( $tags->is_tag_closer() ); + } + + public function test_next_balanced_closer_proceeds_to_correct_tag_for_nested_tag() { + $tags = new WP_Directive_Processor( self::HTML ); + + $tags->next_tag( 'div' ); + $tags->next_tag( 'div' ); + $tags->next_balanced_closer(); + $this->assertSame( 'DIV', $tags->get_tag() ); + $this->assertTrue( $tags->is_tag_closer() ); + } + + public function test_get_inner_html_returns_correct_result() { + $tags = new WP_Directive_Processor( self::HTML ); + + $tags->next_tag( 'section' ); + $this->assertSame( '
inside
', $tags->get_inner_html() ); + } + + public function test_set_inner_html_on_void_element_has_no_effect() { + $tags = new WP_Directive_Processor( self::HTML ); + + $tags->next_tag( 'img' ); + $content = $tags->set_inner_html( 'This is the new img content' ); + $this->assertFalse( $content ); + $this->assertSame( self::HTML, $tags->get_updated_html() ); + } + + public function test_set_inner_html_sets_content_correctly() { + $tags = new WP_Directive_Processor( self::HTML ); + + $tags->next_tag( 'section' ); + $tags->set_inner_html( 'This is the new section content.' ); + $this->assertSame( '
outside
This is the new section content.
', $tags->get_updated_html() ); + } + + public function test_set_inner_html_updates_bookmarks_correctly() { + $tags = new WP_Directive_Processor( self::HTML ); + + $tags->next_tag( 'div' ); + $tags->set_bookmark( 'start' ); + $tags->next_tag( 'img' ); + $this->assertSame( 'IMG', $tags->get_tag() ); + $tags->set_bookmark( 'after' ); + $tags->seek( 'start' ); + + $tags->set_inner_html( 'This is the new div content.' ); + $this->assertSame( '
This is the new div content.
inside
', $tags->get_updated_html() ); + $tags->seek( 'after' ); + $this->assertSame( 'IMG', $tags->get_tag() ); + } + + public function test_set_inner_html_subsequent_updates_on_the_same_tag_work() { + $tags = new WP_Directive_Processor( self::HTML ); + + $tags->next_tag( 'section' ); + $tags->set_inner_html( 'This is the new section content.' ); + $tags->set_inner_html( 'This is the even newer section content.' ); + $this->assertSame( '
outside
This is the even newer section content.
', $tags->get_updated_html() ); + } + + public function test_set_inner_html_followed_by_set_attribute_works() { + $tags = new WP_Directive_Processor( self::HTML ); + + $tags->next_tag( 'section' ); + $tags->set_inner_html( 'This is the new section content.' ); + $tags->set_attribute( 'id', 'thesection' ); + $this->assertSame( '
outside
This is the new section content.
', $tags->get_updated_html() ); + } + + public function test_set_inner_html_preceded_by_set_attribute_works() { + $tags = new WP_Directive_Processor( self::HTML ); + + $tags->next_tag( 'section' ); + $tags->set_attribute( 'id', 'thesection' ); + $tags->set_inner_html( 'This is the new section content.' ); + $this->assertSame( '
outside
This is the new section content.
', $tags->get_updated_html() ); + } + + /** + * TODO: Review this, how that the code is in Gutenberg. + */ + public function test_set_inner_html_invalidates_bookmarks_that_point_to_replaced_content() { + $this->markTestSkipped( "This requires on bookmark invalidation, which is only in GB's WP 6.3 compat layer." ); + + $tags = new WP_Directive_Processor( self::HTML ); + + $tags->next_tag( 'section' ); + $tags->set_bookmark( 'start' ); + $tags->next_tag( 'img' ); + $tags->set_bookmark( 'replaced' ); + $tags->seek( 'start' ); + + $tags->set_inner_html( 'This is the new section content.' ); + $this->assertSame( '
outside
This is the new section content.
', $tags->get_updated_html() ); + + $this->expectExceptionMessage( 'Invalid bookmark name' ); + $successful_seek = $tags->seek( 'replaced' ); + $this->assertFalse( $successful_seek ); + } +} diff --git a/phpunit/experimental/interactivity-api/class-wp-interactivity-store-test.php b/phpunit/experimental/interactivity-api/class-wp-interactivity-store-test.php new file mode 100644 index 00000000000000..84286457f26129 --- /dev/null +++ b/phpunit/experimental/interactivity-api/class-wp-interactivity-store-test.php @@ -0,0 +1,168 @@ +assertEmpty( WP_Interactivity_Store::get_data() ); + } + + public function test_store_can_be_merged() { + $data = array( + 'state' => array( + 'core' => array( + 'a' => 1, + 'b' => 2, + 'nested' => array( + 'c' => 3, + ), + ), + ), + ); + WP_Interactivity_Store::merge_data( $data ); + $this->assertSame( $data, WP_Interactivity_Store::get_data() ); + } + + public function test_store_can_be_extended() { + WP_Interactivity_Store::merge_data( + array( + 'state' => array( + 'core' => array( + 'a' => 1, + ), + ), + ) + ); + WP_Interactivity_Store::merge_data( + array( + 'state' => array( + 'core' => array( + 'b' => 2, + ), + 'custom' => array( + 'c' => 3, + ), + ), + ) + ); + $this->assertSame( + array( + 'state' => array( + 'core' => array( + 'a' => 1, + 'b' => 2, + ), + 'custom' => array( + 'c' => 3, + ), + ), + ), + WP_Interactivity_Store::get_data() + ); + } + + public function test_store_existing_props_should_be_overwritten() { + WP_Interactivity_Store::merge_data( + array( + 'state' => array( + 'core' => array( + 'a' => 1, + ), + ), + ) + ); + WP_Interactivity_Store::merge_data( + array( + 'state' => array( + 'core' => array( + 'a' => 'overwritten', + ), + ), + ) + ); + $this->assertSame( + array( + 'state' => array( + 'core' => array( + 'a' => 'overwritten', + ), + ), + ), + WP_Interactivity_Store::get_data() + ); + } + + public function test_store_existing_indexed_arrays_should_be_replaced() { + WP_Interactivity_Store::merge_data( + array( + 'state' => array( + 'core' => array( + 'a' => array( 1, 2 ), + ), + ), + ) + ); + WP_Interactivity_Store::merge_data( + array( + 'state' => array( + 'core' => array( + 'a' => array( 3, 4 ), + ), + ), + ) + ); + $this->assertSame( + array( + 'state' => array( + 'core' => array( + 'a' => array( 3, 4 ), + ), + ), + ), + WP_Interactivity_Store::get_data() + ); + } + + public function test_store_should_be_correctly_rendered() { + WP_Interactivity_Store::merge_data( + array( + 'state' => array( + 'core' => array( + 'a' => 1, + ), + ), + ) + ); + WP_Interactivity_Store::merge_data( + array( + 'state' => array( + 'core' => array( + 'b' => 2, + ), + ), + ) + ); + ob_start(); + WP_Interactivity_Store::render(); + $rendered = ob_get_clean(); + $this->assertSame( + '', + $rendered + ); + } +} diff --git a/phpunit/experimental/interactivity-api/directive-processing-test.php b/phpunit/experimental/interactivity-api/directive-processing-test.php new file mode 100644 index 00000000000000..4da0a4a85ac3c0 --- /dev/null +++ b/phpunit/experimental/interactivity-api/directive-processing-test.php @@ -0,0 +1,170 @@ +createMock( Helper_Class::class ); + + $test_helper->expects( $this->exactly( 2 ) ) + ->method( 'process_foo_test' ) + ->with( + $this->callback( + function( $p ) { + return 'DIV' === $p->get_tag() && ( + // Either this is a closing tag... + $p->is_tag_closer() || + // ...or it is an open tag, and has the directive attribute set. + ( ! $p->is_tag_closer() && 'abc' === $p->get_attribute( 'foo-test' ) ) + ); + } + ) + ); + + $directives = array( + 'foo-test' => array( $test_helper, 'process_foo_test' ), + ); + + $markup = '
Example:
This is a test>
Here is a nested div
'; + $tags = new WP_HTML_Tag_Processor( $markup ); + gutenberg_interactivity_process_directives( $tags, 'foo-', $directives ); + } + + public function test_directives_with_double_hyphen_processed_correctly() { + $test_helper = $this->createMock( Helper_Class::class ); + $test_helper->expects( $this->atLeastOnce() ) + ->method( 'process_foo_test' ); + + $directives = array( + 'foo-test' => array( $test_helper, 'process_foo_test' ), + ); + + $markup = '
'; + $tags = new WP_HTML_Tag_Processor( $markup ); + gutenberg_interactivity_process_directives( $tags, 'foo-', $directives ); + } +} + +/** + * Tests for the gutenberg_interactivity_evaluate_reference function. + * + * @group interactivity-api + * @covers gutenberg_interactivity_evaluate_reference + */ +class Tests_Utils_Evaluate extends WP_UnitTestCase { + public function test_evaluate_function_should_access_state() { + // Init a simple store. + wp_store( + array( + 'state' => array( + 'core' => array( + 'number' => 1, + 'bool' => true, + 'nested' => array( + 'string' => 'hi', + ), + ), + ), + ) + ); + $this->assertSame( 1, gutenberg_interactivity_evaluate_reference( 'state.core.number' ) ); + $this->assertTrue( gutenberg_interactivity_evaluate_reference( 'state.core.bool' ) ); + $this->assertSame( 'hi', gutenberg_interactivity_evaluate_reference( 'state.core.nested.string' ) ); + $this->assertFalse( gutenberg_interactivity_evaluate_reference( '!state.core.bool' ) ); + } + + public function test_evaluate_function_should_access_passed_context() { + $context = array( + 'local' => array( + 'number' => 2, + 'bool' => false, + 'nested' => array( + 'string' => 'bye', + ), + ), + ); + $this->assertSame( 2, gutenberg_interactivity_evaluate_reference( 'context.local.number', $context ) ); + $this->assertFalse( gutenberg_interactivity_evaluate_reference( 'context.local.bool', $context ) ); + $this->assertTrue( gutenberg_interactivity_evaluate_reference( '!context.local.bool', $context ) ); + $this->assertSame( 'bye', gutenberg_interactivity_evaluate_reference( 'context.local.nested.string', $context ) ); + // Previously defined state is also accessible. + $this->assertSame( 1, gutenberg_interactivity_evaluate_reference( 'state.core.number' ) ); + $this->assertTrue( gutenberg_interactivity_evaluate_reference( 'state.core.bool' ) ); + $this->assertSame( 'hi', gutenberg_interactivity_evaluate_reference( 'state.core.nested.string' ) ); + } + + public function test_evaluate_function_should_return_null_for_unresolved_paths() { + $this->assertNull( gutenberg_interactivity_evaluate_reference( 'this.property.doesnt.exist' ) ); + } + + public function test_evaluate_function_should_execute_anonymous_functions() { + $context = new WP_Directive_Context( array( 'count' => 2 ) ); + $helper = new Helper_Class; + + wp_store( + array( + 'state' => array( + 'count' => 3, + ), + 'selectors' => array( + 'anonymous_function' => function( $store ) { + return $store['state']['count'] + $store['context']['count']; + }, + // Other types of callables should not be executed. + 'function_name' => 'gutenberg_test_process_directives_helper_increment', + 'class_method' => array( $helper, 'increment' ), + 'class_static_method' => 'Helper_Class::static_increment', + 'class_static_method_as_array' => array( 'Helper_Class', 'static_increment' ), + ), + ) + ); + + $this->assertSame( 5, gutenberg_interactivity_evaluate_reference( 'selectors.anonymous_function', $context->get_context() ) ); + $this->assertSame( + 'gutenberg_test_process_directives_helper_increment', + gutenberg_interactivity_evaluate_reference( 'selectors.function_name', $context->get_context() ) + ); + $this->assertSame( + array( $helper, 'increment' ), + gutenberg_interactivity_evaluate_reference( 'selectors.class_method', $context->get_context() ) + ); + $this->assertSame( + 'Helper_Class::static_increment', + gutenberg_interactivity_evaluate_reference( 'selectors.class_static_method', $context->get_context() ) + ); + $this->assertSame( + array( 'Helper_Class', 'static_increment' ), + gutenberg_interactivity_evaluate_reference( 'selectors.class_static_method_as_array', $context->get_context() ) + ); + } +} diff --git a/phpunit/experimental/interactivity-api/directives/wp-bind-test.php b/phpunit/experimental/interactivity-api/directives/wp-bind-test.php new file mode 100644 index 00000000000000..bfb4c428cd9466 --- /dev/null +++ b/phpunit/experimental/interactivity-api/directives/wp-bind-test.php @@ -0,0 +1,46 @@ +'; + $tags = new WP_HTML_Tag_Processor( $markup ); + $tags->next_tag(); + + $context_before = new WP_Directive_Context( array( 'myblock' => array( 'imageSource' => './wordpress.png' ) ) ); + $context = $context_before; + gutenberg_interactivity_process_wp_bind( $tags, $context ); + + $this->assertSame( + '', + $tags->get_updated_html() + ); + $this->assertSame( './wordpress.png', $tags->get_attribute( 'src' ) ); + $this->assertSame( $context_before->get_context(), $context->get_context(), 'data-wp-bind directive changed context' ); + } + + public function test_directive_ignores_empty_bound_attribute() { + $markup = ''; + $tags = new WP_HTML_Tag_Processor( $markup ); + $tags->next_tag(); + + $context_before = new WP_Directive_Context( array( 'myblock' => array( 'imageSource' => './wordpress.png' ) ) ); + $context = $context_before; + gutenberg_interactivity_process_wp_bind( $tags, $context ); + + $this->assertSame( $markup, $tags->get_updated_html() ); + $this->assertNull( $tags->get_attribute( 'src' ) ); + $this->assertSame( $context_before->get_context(), $context->get_context(), 'data-wp-bind directive changed context' ); + } +} diff --git a/phpunit/experimental/interactivity-api/directives/wp-class-test.php b/phpunit/experimental/interactivity-api/directives/wp-class-test.php new file mode 100644 index 00000000000000..419546c6d9ef8b --- /dev/null +++ b/phpunit/experimental/interactivity-api/directives/wp-class-test.php @@ -0,0 +1,98 @@ +Test'; + $tags = new WP_HTML_Tag_Processor( $markup ); + $tags->next_tag(); + + $context_before = new WP_Directive_Context( array( 'myblock' => array( 'isRed' => true ) ) ); + $context = $context_before; + gutenberg_interactivity_process_wp_class( $tags, $context ); + + $this->assertSame( + '
Test
', + $tags->get_updated_html() + ); + $this->assertStringContainsString( 'red', $tags->get_attribute( 'class' ) ); + $this->assertSame( $context_before->get_context(), $context->get_context(), 'data-wp-class directive changed context' ); + } + + public function test_directive_removes_class() { + $markup = '
Test
'; + $tags = new WP_HTML_Tag_Processor( $markup ); + $tags->next_tag(); + + $context_before = new WP_Directive_Context( array( 'myblock' => array( 'isBlue' => false ) ) ); + $context = $context_before; + gutenberg_interactivity_process_wp_class( $tags, $context ); + + $this->assertSame( + '
Test
', + $tags->get_updated_html() + ); + $this->assertStringNotContainsString( 'blue', $tags->get_attribute( 'class' ) ); + $this->assertSame( $context_before->get_context(), $context->get_context(), 'data-wp-class directive changed context' ); + } + + public function test_directive_removes_empty_class_attribute() { + $markup = '
Test
'; + $tags = new WP_HTML_Tag_Processor( $markup ); + $tags->next_tag(); + + $context_before = new WP_Directive_Context( array( 'myblock' => array( 'isBlue' => false ) ) ); + $context = $context_before; + gutenberg_interactivity_process_wp_class( $tags, $context ); + + $this->assertSame( + // WP_HTML_Tag_Processor has a TODO note to prune whitespace after classname removal. + '
Test
', + $tags->get_updated_html() + ); + $this->assertNull( $tags->get_attribute( 'class' ) ); + $this->assertSame( $context_before->get_context(), $context->get_context(), 'data-wp-class directive changed context' ); + } + + public function test_directive_does_not_remove_non_existant_class() { + $markup = '
Test
'; + $tags = new WP_HTML_Tag_Processor( $markup ); + $tags->next_tag(); + + $context_before = new WP_Directive_Context( array( 'myblock' => array( 'isBlue' => false ) ) ); + $context = $context_before; + gutenberg_interactivity_process_wp_class( $tags, $context ); + + $this->assertSame( + '
Test
', + $tags->get_updated_html() + ); + $this->assertSame( 'green red', $tags->get_attribute( 'class' ) ); + $this->assertSame( $context_before->get_context(), $context->get_context(), 'data-wp-class directive changed context' ); + } + + public function test_directive_ignores_empty_class_name() { + $markup = '
Test
'; + $tags = new WP_HTML_Tag_Processor( $markup ); + $tags->next_tag(); + + $context_before = new WP_Directive_Context( array( 'myblock' => array( 'isRed' => true ) ) ); + $context = $context_before; + gutenberg_interactivity_process_wp_class( $tags, $context ); + + $this->assertSame( $markup, $tags->get_updated_html() ); + $this->assertStringNotContainsString( 'red', $tags->get_attribute( 'class' ) ); + $this->assertSame( $context_before->get_context(), $context->get_context(), 'data-wp-class directive changed context' ); + } +} diff --git a/phpunit/experimental/interactivity-api/directives/wp-context-test.php b/phpunit/experimental/interactivity-api/directives/wp-context-test.php new file mode 100644 index 00000000000000..90ffb7dd9bf296 --- /dev/null +++ b/phpunit/experimental/interactivity-api/directives/wp-context-test.php @@ -0,0 +1,77 @@ + array( 'open' => false ), + 'otherblock' => array( 'somekey' => 'somevalue' ), + ) + ); + + $markup = '
'; + $tags = new WP_HTML_Tag_Processor( $markup ); + $tags->next_tag(); + + gutenberg_interactivity_process_wp_context( $tags, $context ); + + $this->assertSame( + array( + 'myblock' => array( 'open' => true ), + 'otherblock' => array( 'somekey' => 'somevalue' ), + ), + $context->get_context() + ); + } + + public function test_directive_resets_context_correctly_upon_closing_tag() { + $context = new WP_Directive_Context( + array( 'my-key' => 'original-value' ) + ); + + $context->set_context( + array( 'my-key' => 'new-value' ) + ); + + $markup = '
'; + $tags = new WP_HTML_Tag_Processor( $markup ); + $tags->next_tag( array( 'tag_closers' => 'visit' ) ); + + gutenberg_interactivity_process_wp_context( $tags, $context ); + + $this->assertSame( + array( 'my-key' => 'original-value' ), + $context->get_context() + ); + } + + public function test_directive_doesnt_throw_on_malformed_context_objects() { + $context = new WP_Directive_Context( + array( 'my-key' => 'some-value' ) + ); + + $markup = '
'; + $tags = new WP_HTML_Tag_Processor( $markup ); + $tags->next_tag(); + + gutenberg_interactivity_process_wp_context( $tags, $context ); + + $this->assertSame( + array( 'my-key' => 'some-value' ), + $context->get_context() + ); + } + +} diff --git a/phpunit/experimental/interactivity-api/directives/wp-style-test.php b/phpunit/experimental/interactivity-api/directives/wp-style-test.php new file mode 100644 index 00000000000000..8942559b2fe89f --- /dev/null +++ b/phpunit/experimental/interactivity-api/directives/wp-style-test.php @@ -0,0 +1,46 @@ +Test
'; + $tags = new WP_HTML_Tag_Processor( $markup ); + $tags->next_tag(); + + $context_before = new WP_Directive_Context( array( 'myblock' => array( 'color' => 'green' ) ) ); + $context = $context_before; + gutenberg_interactivity_process_wp_style( $tags, $context ); + + $this->assertSame( + '
Test
', + $tags->get_updated_html() + ); + $this->assertStringContainsString( 'color: green;', $tags->get_attribute( 'style' ) ); + $this->assertSame( $context_before->get_context(), $context->get_context(), 'data-wp-style directive changed context' ); + } + + public function test_directive_ignores_empty_style() { + $markup = '
Test
'; + $tags = new WP_HTML_Tag_Processor( $markup ); + $tags->next_tag(); + + $context_before = new WP_Directive_Context( array( 'myblock' => array( 'color' => 'green' ) ) ); + $context = $context_before; + gutenberg_interactivity_process_wp_style( $tags, $context ); + + $this->assertSame( $markup, $tags->get_updated_html() ); + $this->assertStringNotContainsString( 'color: green;', $tags->get_attribute( 'style' ) ); + $this->assertSame( $context_before->get_context(), $context->get_context(), 'data-wp-style directive changed context' ); + } +} diff --git a/phpunit/experimental/interactivity-api/directives/wp-text-test.php b/phpunit/experimental/interactivity-api/directives/wp-text-test.php new file mode 100644 index 00000000000000..81d2d0f370a64b --- /dev/null +++ b/phpunit/experimental/interactivity-api/directives/wp-text-test.php @@ -0,0 +1,45 @@ +'; + + $tags = new WP_Directive_Processor( $markup ); + $tags->next_tag(); + + $context_before = new WP_Directive_Context( array( 'myblock' => array( 'someText' => 'The HTML tag
produces a line break.' ) ) ); + $context = clone $context_before; + gutenberg_interactivity_process_wp_text( $tags, $context ); + + $expected_markup = '
The HTML tag <br> produces a line break.
'; + $this->assertSame( $expected_markup, $tags->get_updated_html() ); + $this->assertSame( $context_before->get_context(), $context->get_context(), 'data-wp-text directive changed context' ); + } + + public function test_directive_overwrites_inner_html_based_on_attribute_value() { + $markup = '
Lorem ipsum dolor sit.
'; + + $tags = new WP_Directive_Processor( $markup ); + $tags->next_tag(); + + $context_before = new WP_Directive_Context( array( 'myblock' => array( 'someText' => 'Honi soit qui mal y pense.' ) ) ); + $context = clone $context_before; + gutenberg_interactivity_process_wp_text( $tags, $context ); + + $expected_markup = '
Honi soit qui mal y pense.
'; + $this->assertSame( $expected_markup, $tags->get_updated_html() ); + $this->assertSame( $context_before->get_context(), $context->get_context(), 'data-wp-text directive changed context' ); + } +} diff --git a/test/e2e/specs/interactivity/directive-bind.spec.ts b/test/e2e/specs/interactivity/directive-bind.spec.ts new file mode 100644 index 00000000000000..67ee1232a6798e --- /dev/null +++ b/test/e2e/specs/interactivity/directive-bind.spec.ts @@ -0,0 +1,96 @@ +/** + * Internal dependencies + */ +import { test, expect } from './fixtures'; + +test.describe( 'data-wp-bind', () => { + test.beforeAll( async ( { interactivityUtils: utils } ) => { + await utils.activatePlugins(); + await utils.addPostWithBlock( 'test/directive-bind' ); + } ); + + test.beforeEach( async ( { interactivityUtils: utils, page } ) => { + await page.goto( utils.getLink( 'test/directive-bind' ) ); + } ); + + test.afterAll( async ( { interactivityUtils: utils } ) => { + await utils.deactivatePlugins(); + await utils.deleteAllPosts(); + } ); + + test( 'add missing href at hydration', async ( { page } ) => { + const el = page.getByTestId( 'add missing href at hydration' ); + await expect( el ).toHaveAttribute( 'href', '/some-url' ); + } ); + + test( 'change href at hydration', async ( { page } ) => { + const el = page.getByTestId( 'change href at hydration' ); + await expect( el ).toHaveAttribute( 'href', '/some-url' ); + } ); + + test( 'update missing href at hydration', async ( { page } ) => { + const el = page.getByTestId( 'add missing href at hydration' ); + await expect( el ).toHaveAttribute( 'href', '/some-url' ); + await page.getByTestId( 'toggle' ).click(); + await expect( el ).toHaveAttribute( 'href', '/some-other-url' ); + } ); + + test( 'add missing checked at hydration', async ( { page } ) => { + const el = page.getByTestId( 'add missing checked at hydration' ); + await expect( el ).toHaveAttribute( 'checked', '' ); + } ); + + test( 'remove existing checked at hydration', async ( { page } ) => { + const el = page.getByTestId( 'remove existing checked at hydration' ); + await expect( el ).not.toHaveAttribute( 'checked', '' ); + } ); + + test( 'update existing checked', async ( { page } ) => { + const el = page.getByTestId( 'add missing checked at hydration' ); + const el2 = page.getByTestId( 'remove existing checked at hydration' ); + let checked = await el.evaluate( + ( element: HTMLInputElement ) => element.checked + ); + let checked2 = await el2.evaluate( + ( element: HTMLInputElement ) => element.checked + ); + expect( checked ).toBe( true ); + expect( checked2 ).toBe( false ); + await page.getByTestId( 'toggle' ).click(); + checked = await el.evaluate( + ( element: HTMLInputElement ) => element.checked + ); + checked2 = await el2.evaluate( + ( element: HTMLInputElement ) => element.checked + ); + expect( checked ).toBe( false ); + expect( checked2 ).toBe( true ); + } ); + + test( 'nested binds', async ( { page } ) => { + const el = page.getByTestId( 'nested binds - 1' ); + await expect( el ).toHaveAttribute( 'href', '/some-url' ); + const el2 = page.getByTestId( 'nested binds - 2' ); + await expect( el2 ).toHaveAttribute( 'width', '1' ); + await page.getByTestId( 'toggle' ).click(); + await expect( el ).toHaveAttribute( 'href', '/some-other-url' ); + await expect( el2 ).toHaveAttribute( 'width', '2' ); + } ); + + test( 'check enumerated attributes with true/false values', async ( { + page, + } ) => { + const el = page.getByTestId( + 'check enumerated attributes with true/false exist and have a string value' + ); + await expect( el ).toHaveAttribute( 'hidden', '' ); + await expect( el ).toHaveAttribute( 'aria-hidden', 'true' ); + await expect( el ).toHaveAttribute( 'aria-expanded', 'false' ); + await expect( el ).toHaveAttribute( 'data-some-value', 'false' ); + await page.getByTestId( 'toggle' ).click(); + await expect( el ).not.toHaveAttribute( 'hidden', '' ); + await expect( el ).toHaveAttribute( 'aria-hidden', 'false' ); + await expect( el ).toHaveAttribute( 'aria-expanded', 'true' ); + await expect( el ).toHaveAttribute( 'data-some-value', 'true' ); + } ); +} ); diff --git a/test/e2e/specs/interactivity/directive-effect.spec.ts b/test/e2e/specs/interactivity/directive-effect.spec.ts new file mode 100644 index 00000000000000..2ec52444cfd9b8 --- /dev/null +++ b/test/e2e/specs/interactivity/directive-effect.spec.ts @@ -0,0 +1,39 @@ +/** + * Internal dependencies + */ +import { test, expect } from './fixtures'; + +test.describe( 'data-wp-effect', () => { + test.beforeAll( async ( { interactivityUtils: utils } ) => { + await utils.activatePlugins(); + await utils.addPostWithBlock( 'test/directive-effect' ); + } ); + + test.beforeEach( async ( { interactivityUtils: utils, page } ) => { + await page.goto( utils.getLink( 'test/directive-effect' ) ); + } ); + + test.afterAll( async ( { interactivityUtils: utils } ) => { + await utils.deactivatePlugins(); + await utils.deleteAllPosts(); + } ); + + test( 'check that effect runs when it is added', async ( { page } ) => { + const el = page.getByTestId( 'element in the DOM' ); + await expect( el ).toContainText( 'element is in the DOM' ); + } ); + + test( 'check that effect runs when it is removed', async ( { page } ) => { + await page.getByTestId( 'toggle' ).click(); + const el = page.getByTestId( 'element in the DOM' ); + await expect( el ).toContainText( 'element is not in the DOM' ); + } ); + + test( 'change focus after DOM changes', async ( { page } ) => { + const el = page.getByTestId( 'input' ); + await expect( el ).toBeFocused(); + await page.getByTestId( 'toggle' ).click(); + await page.getByTestId( 'toggle' ).click(); + await expect( el ).toBeFocused(); + } ); +} ); diff --git a/test/e2e/specs/interactivity/directive-priorities.spec.ts b/test/e2e/specs/interactivity/directive-priorities.spec.ts new file mode 100644 index 00000000000000..1734d6f5aecc36 --- /dev/null +++ b/test/e2e/specs/interactivity/directive-priorities.spec.ts @@ -0,0 +1,84 @@ +/** + * Internal dependencies + */ +import { test, expect } from './fixtures'; + +test.describe( 'Directives (w/ priority)', () => { + test.beforeAll( async ( { interactivityUtils: utils } ) => { + await utils.activatePlugins(); + await utils.addPostWithBlock( 'test/directive-priorities' ); + } ); + + test.beforeEach( async ( { interactivityUtils: utils, page } ) => { + await page.goto( utils.getLink( 'test/directive-priorities' ) ); + } ); + + test.afterAll( async ( { interactivityUtils: utils } ) => { + await utils.deactivatePlugins(); + await utils.deleteAllPosts(); + } ); + + test( 'should run in priority order', async ( { page } ) => { + const executionOrder = page.getByTestId( 'execution order' ); + await expect( executionOrder ).toHaveText( + 'context, attribute, text, children' + ); + } ); + + test( 'should wrap those with less priority', async ( { page } ) => { + // Check that attribute value is correctly received from Provider. + const element = page.getByTestId( 'test directives' ); + await expect( element ).toHaveAttribute( + 'data-attribute', + 'from context' + ); + + // Check that text value is correctly received from Provider, and text + // wrapped with an element with `data-testid=text`. + const text = element.getByTestId( 'text' ); + await expect( text ).toHaveText( 'from context' ); + } ); + + test( 'should propagate element modifications top-down', async ( { + page, + } ) => { + const executionOrder = page.getByTestId( 'execution order' ); + const element = page.getByTestId( 'test directives' ); + const text = element.getByTestId( 'text' ); + + // Get buttons. + const updateAttribute = element.getByRole( 'button', { + name: 'Update attribute', + } ); + const updateText = element.getByRole( 'button', { + name: 'Update text', + } ); + + // Modify `attribute` inside context. This triggers a re-render for the + // component that wraps the `attribute` directive, evaluating it again. + // Nested components are re-rendered as well, so their directives are + // also re-evaluated (note how `text` and `children` have run). + await updateAttribute.click(); + await expect( element ).toHaveAttribute( 'data-attribute', 'updated' ); + await expect( executionOrder ).toHaveText( + [ + 'context, attribute, text, children', + 'attribute, text, children', + ].join( ', ' ) + ); + + // Modify `text` inside context. This triggers a re-render of the + // component that wraps the `text` directive. In this case, only + // `children` run as well, right after `text`. + await updateText.click(); + await expect( element ).toHaveAttribute( 'data-attribute', 'updated' ); + await expect( text ).toHaveText( 'updated' ); + await expect( executionOrder ).toHaveText( + [ + 'context, attribute, text, children', + 'attribute, text, children', + 'text, children', + ].join( ', ' ) + ); + } ); +} ); diff --git a/test/e2e/specs/interactivity/directives-class.spec.ts b/test/e2e/specs/interactivity/directives-class.spec.ts new file mode 100644 index 00000000000000..c0ec77e14d0567 --- /dev/null +++ b/test/e2e/specs/interactivity/directives-class.spec.ts @@ -0,0 +1,101 @@ +/** + * Internal dependencies + */ +import { test, expect } from './fixtures'; + +test.describe( 'data-wp-class', () => { + test.beforeAll( async ( { interactivityUtils: utils } ) => { + await utils.activatePlugins(); + await utils.addPostWithBlock( 'test/directive-class' ); + } ); + + test.beforeEach( async ( { interactivityUtils: utils, page } ) => { + await page.goto( utils.getLink( 'test/directive-class' ) ); + } ); + + test.afterAll( async ( { interactivityUtils: utils } ) => { + await utils.deactivatePlugins(); + await utils.deleteAllPosts(); + } ); + + test( 'remove class if callback returns falsy value', async ( { + page, + } ) => { + const el = page.getByTestId( + 'remove class if callback returns falsy value' + ); + await expect( el ).toHaveClass( 'bar' ); + await page.getByTestId( 'toggle falseValue' ).click(); + await expect( el ).toHaveClass( 'foo bar' ); + await page.getByTestId( 'toggle falseValue' ).click(); + await expect( el ).toHaveClass( 'bar' ); + } ); + + test( 'add class if callback returns truthy value', async ( { page } ) => { + const el = page.getByTestId( + 'add class if callback returns truthy value' + ); + await expect( el ).toHaveClass( 'foo bar' ); + await page.getByTestId( 'toggle trueValue' ).click(); + await expect( el ).toHaveClass( 'foo' ); + await page.getByTestId( 'toggle trueValue' ).click(); + await expect( el ).toHaveClass( 'foo bar' ); + } ); + + test( 'handles multiple classes and callbacks', async ( { page } ) => { + const el = page.getByTestId( 'handles multiple classes and callbacks' ); + await expect( el ).toHaveClass( 'bar baz' ); + await page.getByTestId( 'toggle trueValue' ).click(); + await expect( el ).toHaveClass( '' ); + await page.getByTestId( 'toggle trueValue' ).click(); + await expect( el ).toHaveClass( 'bar baz' ); + await page.getByTestId( 'toggle falseValue' ).click(); + await expect( el ).toHaveClass( 'foo bar baz' ); + await page.getByTestId( 'toggle trueValue' ).click(); + await expect( el ).toHaveClass( 'foo' ); + } ); + + test( 'handles class names that are contained inside other class names', async ( { + page, + } ) => { + const el = page.getByTestId( + 'handles class names that are contained inside other class names' + ); + await expect( el ).toHaveClass( 'foo-bar' ); + await page.getByTestId( 'toggle falseValue' ).click(); + await expect( el ).toHaveClass( 'foo foo-bar' ); + await page.getByTestId( 'toggle trueValue' ).click(); + await expect( el ).toHaveClass( 'foo' ); + } ); + + test( 'can toggle class in the middle', async ( { page } ) => { + const el = page.getByTestId( 'can toggle class in the middle' ); + await expect( el ).toHaveClass( 'foo bar baz' ); + await page.getByTestId( 'toggle trueValue' ).click(); + await expect( el ).toHaveClass( 'foo baz' ); + await page.getByTestId( 'toggle trueValue' ).click(); + await expect( el ).toHaveClass( 'foo bar baz' ); + } ); + + test( 'can toggle class when class attribute is missing', async ( { + page, + } ) => { + const el = page.getByTestId( + 'can toggle class when class attribute is missing' + ); + await expect( el ).toHaveClass( '' ); + await page.getByTestId( 'toggle falseValue' ).click(); + await expect( el ).toHaveClass( 'foo' ); + await page.getByTestId( 'toggle falseValue' ).click(); + await expect( el ).toHaveClass( '' ); + } ); + + test( 'can use context values', async ( { page } ) => { + const el = page.getByTestId( 'can use context values' ); + await expect( el ).toHaveClass( '' ); + await page.getByTestId( 'toggle context false value' ).click(); + await expect( el ).toHaveClass( 'foo' ); + await page.getByTestId( 'toggle context false value' ).click(); + await expect( el ).toHaveClass( '' ); + } ); +} ); diff --git a/test/e2e/specs/interactivity/directives-context.spec.ts b/test/e2e/specs/interactivity/directives-context.spec.ts new file mode 100644 index 00000000000000..5c74e8054bf19d --- /dev/null +++ b/test/e2e/specs/interactivity/directives-context.spec.ts @@ -0,0 +1,165 @@ +/** + * External dependencies + */ +import type { Locator } from '@playwright/test'; +/** + * Internal dependencies + */ +import { test, expect } from './fixtures'; + +const parseContent = async ( loc: Locator ) => + JSON.parse( ( await loc.textContent() ) || '' ); + +test.describe( 'data-wp-context', () => { + test.beforeAll( async ( { interactivityUtils: utils } ) => { + await utils.activatePlugins(); + await utils.addPostWithBlock( 'test/directive-context' ); + } ); + + test.beforeEach( async ( { interactivityUtils: utils, page } ) => { + await page.goto( utils.getLink( 'test/directive-context' ) ); + } ); + + test.afterAll( async ( { interactivityUtils: utils } ) => { + await utils.deactivatePlugins(); + await utils.deleteAllPosts(); + } ); + + test( 'is correctly initialized', async ( { page } ) => { + const parentContext = await parseContent( + page.getByTestId( 'parent context' ) + ); + + expect( parentContext ).toMatchObject( { + prop1: 'parent', + prop2: 'parent', + obj: { prop4: 'parent', prop5: 'parent' }, + array: [ 1, 2, 3 ], + } ); + } ); + + test( 'is correctly extended', async ( { page } ) => { + const childContext = await parseContent( + page.getByTestId( 'child context' ) + ); + + expect( childContext ).toMatchObject( { + prop1: 'parent', + prop2: 'child', + prop3: 'child', + obj: { prop4: 'parent', prop5: 'child', prop6: 'child' }, + array: [ 4, 5, 6 ], + } ); + } ); + + test( 'changes in inherited properties are reflected (child)', async ( { + page, + } ) => { + await page.getByTestId( 'child prop1' ).click(); + await page.getByTestId( 'child obj.prop4' ).click(); + + const childContext = await parseContent( + page.getByTestId( 'child context' ) + ); + + expect( childContext.prop1 ).toBe( 'modifiedFromChild' ); + expect( childContext.obj.prop4 ).toBe( 'modifiedFromChild' ); + + const parentContext = await parseContent( + page.getByTestId( 'parent context' ) + ); + + expect( parentContext.prop1 ).toBe( 'modifiedFromChild' ); + expect( parentContext.obj.prop4 ).toBe( 'modifiedFromChild' ); + } ); + + test( 'changes in inherited properties are reflected (parent)', async ( { + page, + } ) => { + await page.getByTestId( 'parent prop1' ).click(); + await page.getByTestId( 'parent obj.prop4' ).click(); + + const childContext = await parseContent( + page.getByTestId( 'child context' ) + ); + + expect( childContext.prop1 ).toBe( 'modifiedFromParent' ); + expect( childContext.obj.prop4 ).toBe( 'modifiedFromParent' ); + + const parentContext = await parseContent( + page.getByTestId( 'parent context' ) + ); + + expect( parentContext.prop1 ).toBe( 'modifiedFromParent' ); + expect( parentContext.obj.prop4 ).toBe( 'modifiedFromParent' ); + } ); + + test( 'changes in shadowed properties do not leak (child)', async ( { + page, + } ) => { + await page.getByTestId( 'child prop2' ).click(); + await page.getByTestId( 'child obj.prop5' ).click(); + + const childContext = await parseContent( + page.getByTestId( 'child context' ) + ); + + expect( childContext.prop2 ).toBe( 'modifiedFromChild' ); + expect( childContext.obj.prop5 ).toBe( 'modifiedFromChild' ); + + const parentContext = await parseContent( + page.getByTestId( 'parent context' ) + ); + + expect( parentContext.prop2 ).toBe( 'parent' ); + expect( parentContext.obj.prop5 ).toBe( 'parent' ); + } ); + + test( 'changes in shadowed properties do not leak (parent)', async ( { + page, + } ) => { + await page.getByTestId( 'parent prop2' ).click(); + await page.getByTestId( 'parent obj.prop5' ).click(); + + const childContext = await parseContent( + page.getByTestId( 'child context' ) + ); + + expect( childContext.prop2 ).toBe( 'child' ); + expect( childContext.obj.prop5 ).toBe( 'child' ); + + const parentContext = await parseContent( + page.getByTestId( 'parent context' ) + ); + + expect( parentContext.prop2 ).toBe( 'modifiedFromParent' ); + expect( parentContext.obj.prop5 ).toBe( 'modifiedFromParent' ); + } ); + + test( 'Array properties are shadowed', async ( { page } ) => { + const parentContext = await parseContent( + page.getByTestId( 'parent context' ) + ); + + const childContext = await parseContent( + page.getByTestId( 'child context' ) + ); + + expect( parentContext.array ).toMatchObject( [ 1, 2, 3 ] ); + expect( childContext.array ).toMatchObject( [ 4, 5, 6 ] ); + } ); + + test( 'can be accessed in other directives on the same element', async ( { + page, + } ) => { + const element = page.getByTestId( 'context & other directives' ); + await expect( element ).toHaveText( 'Text 1' ); + await expect( element ).toHaveAttribute( 'value', 'Text 1' ); + await element.click(); + await expect( element ).toHaveText( 'Text 2' ); + await expect( element ).toHaveAttribute( 'value', 'Text 2' ); + await element.click(); + await expect( element ).toHaveText( 'Text 1' ); + await expect( element ).toHaveAttribute( 'value', 'Text 1' ); + } ); +} ); diff --git a/test/e2e/specs/interactivity/directives-show.spec.ts b/test/e2e/specs/interactivity/directives-show.spec.ts new file mode 100644 index 00000000000000..7e25332ae143c5 --- /dev/null +++ b/test/e2e/specs/interactivity/directives-show.spec.ts @@ -0,0 +1,59 @@ +/** + * Internal dependencies + */ +import { test, expect } from './fixtures'; + +test.describe( 'data-wp-show', () => { + test.beforeAll( async ( { interactivityUtils: utils } ) => { + await utils.activatePlugins(); + await utils.addPostWithBlock( 'test/directive-show' ); + } ); + test.beforeEach( async ( { interactivityUtils: utils, page } ) => { + await page.goto( utils.getLink( 'test/directive-show' ) ); + } ); + test.afterAll( async ( { interactivityUtils: utils } ) => { + await utils.deactivatePlugins(); + await utils.deleteAllPosts(); + } ); + + test( 'show if callback returns truthy value', async ( { page } ) => { + const el = page.getByTestId( 'show if callback returns truthy value' ); + await expect( el ).toBeVisible(); + } ); + + test( 'do not show if callback returns falsy value', async ( { page } ) => { + const el = page.getByTestId( + 'do not show if callback returns false value' + ); + await expect( el ).toBeHidden(); + } ); + + test( 'hide when toggling truthy value to falsy', async ( { page } ) => { + const el = page.getByTestId( 'show if callback returns truthy value' ); + await expect( el ).toBeVisible(); + await page.getByTestId( 'toggle trueValue' ).click(); + await expect( el ).toBeHidden(); + await page.getByTestId( 'toggle trueValue' ).click(); + await expect( el ).toBeVisible(); + } ); + + test( 'show when toggling false value to truthy', async ( { page } ) => { + const el = page.getByTestId( + 'do not show if callback returns false value' + ); + await expect( el ).toBeHidden(); + await page.getByTestId( 'toggle falseValue' ).click(); + await expect( el ).toBeVisible(); + await page.getByTestId( 'toggle falseValue' ).click(); + await expect( el ).toBeHidden(); + } ); + + test( 'can use context values', async ( { page } ) => { + const el = page.getByTestId( 'can use context values' ); + await expect( el ).toBeHidden(); + await page.getByTestId( 'toggle context false value' ).click(); + await expect( el ).toBeVisible(); + await page.getByTestId( 'toggle context false value' ).click(); + await expect( el ).toBeHidden(); + } ); +} ); diff --git a/test/e2e/specs/interactivity/directives-text.spec.ts b/test/e2e/specs/interactivity/directives-text.spec.ts new file mode 100644 index 00000000000000..8e83be26de15ca --- /dev/null +++ b/test/e2e/specs/interactivity/directives-text.spec.ts @@ -0,0 +1,36 @@ +/** + * Internal dependencies + */ +import { test, expect } from './fixtures'; + +test.describe( 'data-wp-text', () => { + test.beforeAll( async ( { interactivityUtils: utils } ) => { + await utils.activatePlugins(); + await utils.addPostWithBlock( 'test/directive-text' ); + } ); + test.beforeEach( async ( { interactivityUtils: utils, page } ) => { + await page.goto( utils.getLink( 'test/directive-text' ) ); + } ); + test.afterAll( async ( { interactivityUtils: utils } ) => { + await utils.deactivatePlugins(); + await utils.deleteAllPosts(); + } ); + + test( 'show proper text reading from state', async ( { page } ) => { + const el = page.getByTestId( 'show state text' ); + await expect( el ).toHaveText( 'Text 1' ); + await page.getByTestId( 'toggle state text' ).click(); + await expect( el ).toHaveText( 'Text 2' ); + await page.getByTestId( 'toggle state text' ).click(); + await expect( el ).toHaveText( 'Text 1' ); + } ); + + test( 'show proper text reading from context', async ( { page } ) => { + const el = page.getByTestId( 'show context text' ); + await expect( el ).toHaveText( 'Text 1' ); + await page.getByTestId( 'toggle context text' ).click(); + await expect( el ).toHaveText( 'Text 2' ); + await page.getByTestId( 'toggle context text' ).click(); + await expect( el ).toHaveText( 'Text 1' ); + } ); +} ); diff --git a/test/e2e/specs/interactivity/fixtures/index.ts b/test/e2e/specs/interactivity/fixtures/index.ts new file mode 100644 index 00000000000000..607221ffb1ec43 --- /dev/null +++ b/test/e2e/specs/interactivity/fixtures/index.ts @@ -0,0 +1,25 @@ +/** + * WordPress dependencies + */ +import { test as base } from '@wordpress/e2e-test-utils-playwright'; +export { expect } from '@wordpress/e2e-test-utils-playwright'; + +/** + * Internal dependencies + */ +import InteractivityUtils from './interactivity-utils'; + +type Fixtures = { + interactivityUtils: InteractivityUtils; +}; + +export const test = base.extend< Fixtures >( { + interactivityUtils: [ + async ( { requestUtils }, use ) => { + await use( new InteractivityUtils( { requestUtils } ) ); + }, + // @ts-ignore: The required type is 'test', but can be 'worker' too. See + // https://playwright.dev/docs/test-fixtures#worker-scoped-fixtures + { scope: 'worker' }, + ], +} ); diff --git a/test/e2e/specs/interactivity/fixtures/interactivity-utils.ts b/test/e2e/specs/interactivity/fixtures/interactivity-utils.ts new file mode 100644 index 00000000000000..9d83c93650d403 --- /dev/null +++ b/test/e2e/specs/interactivity/fixtures/interactivity-utils.ts @@ -0,0 +1,60 @@ +/** + * WordPress dependencies + */ +import type { RequestUtils } from '@wordpress/e2e-test-utils-playwright'; + +export default class InteractivityUtils { + links: Map< string, string >; + requestUtils: RequestUtils; + + constructor( { requestUtils }: { requestUtils: RequestUtils } ) { + this.links = new Map(); + this.requestUtils = requestUtils; + } + + getLink( blockName: string ) { + const link = this.links.get( blockName ); + if ( ! link ) { + throw new Error( + `No link found for post with block '${ blockName }'` + ); + } + + // Add an extra param to disable directives SSR. This is required at + // this moment, as SSR for directives is not stabilized yet and we need + // to ensure hydration works, even when the SSR'ed HTML is not correct. + const url = new URL( link ); + url.searchParams.append( 'disable_directives_ssr', 'true' ); + return url.href; + } + + async addPostWithBlock( blockName: string ) { + const payload = { + content: ``, + status: 'publish' as 'publish', + date_gmt: '2023-01-01T00:00:00', + }; + + const { link } = await this.requestUtils.createPost( payload ); + this.links.set( blockName, link ); + } + + async deleteAllPosts() { + await this.requestUtils.deleteAllPosts(); + this.links.clear(); + } + + async activatePlugins() { + await this.requestUtils.activateTheme( 'emptytheme' ); + await this.requestUtils.activatePlugin( + 'gutenberg-test-interactive-blocks' + ); + } + + async deactivatePlugins() { + await this.requestUtils.activateTheme( 'twentytwentyone' ); + await this.requestUtils.deactivatePlugin( + 'gutenberg-test-interactive-blocks' + ); + } +} diff --git a/test/e2e/specs/interactivity/negation-operator.spec.ts b/test/e2e/specs/interactivity/negation-operator.spec.ts new file mode 100644 index 00000000000000..da5670a0e57824 --- /dev/null +++ b/test/e2e/specs/interactivity/negation-operator.spec.ts @@ -0,0 +1,44 @@ +/** + * Internal dependencies + */ +import { test, expect } from './fixtures'; + +test.describe( 'negation-operator', () => { + test.beforeAll( async ( { interactivityUtils: utils } ) => { + await utils.activatePlugins(); + await utils.addPostWithBlock( 'test/negation-operator' ); + } ); + + test.beforeEach( async ( { interactivityUtils: utils, page } ) => { + await page.goto( utils.getLink( 'test/negation-operator' ) ); + } ); + + test.afterAll( async ( { interactivityUtils: utils } ) => { + await utils.deactivatePlugins(); + await utils.deleteAllPosts(); + } ); + + test( 'add hidden attribute when !state.active', async ( { page } ) => { + const el = page.getByTestId( + 'add hidden attribute if state is not active' + ); + + await expect( el ).toHaveAttribute( 'hidden', '' ); + await page.getByTestId( 'toggle active value' ).click(); + await expect( el ).not.toHaveAttribute( 'hidden', '' ); + await page.getByTestId( 'toggle active value' ).click(); + await expect( el ).toHaveAttribute( 'hidden', '' ); + } ); + + test( 'add hidden attribute when !selectors.active', async ( { page } ) => { + const el = page.getByTestId( + 'add hidden attribute if selector is not active' + ); + + await expect( el ).toHaveAttribute( 'hidden', '' ); + await page.getByTestId( 'toggle active value' ).click(); + await expect( el ).not.toHaveAttribute( 'hidden', '' ); + await page.getByTestId( 'toggle active value' ).click(); + await expect( el ).toHaveAttribute( 'hidden', '' ); + } ); +} ); diff --git a/test/e2e/specs/interactivity/store-tag.spec.ts b/test/e2e/specs/interactivity/store-tag.spec.ts new file mode 100644 index 00000000000000..80f9acfad93358 --- /dev/null +++ b/test/e2e/specs/interactivity/store-tag.spec.ts @@ -0,0 +1,86 @@ +/** + * Internal dependencies + */ +import { test, expect } from './fixtures'; + +test.describe( 'store tag', () => { + test.beforeAll( async ( { interactivityUtils: utils } ) => { + await utils.activatePlugins(); + await utils.addPostWithBlock( 'test/store-tag {"condition":"ok"}' ); + await utils.addPostWithBlock( + 'test/store-tag {"condition":"missing"}' + ); + await utils.addPostWithBlock( + 'test/store-tag {"condition":"corrupted-json"}' + ); + await utils.addPostWithBlock( + 'test/store-tag {"condition":"invalid-state"}' + ); + } ); + + test.afterAll( async ( { interactivityUtils: utils } ) => { + await utils.deactivatePlugins(); + await utils.deleteAllPosts(); + } ); + + test( 'hydrates when it is well defined', async ( { + interactivityUtils: utils, + page, + } ) => { + const block = 'test/store-tag {"condition":"ok"}'; + await page.goto( utils.getLink( block ) ); + + const value = page.getByTestId( 'counter value' ); + const double = page.getByTestId( 'counter double' ); + const clicks = page.getByTestId( 'counter clicks' ); + + await expect( value ).toHaveText( '3' ); + await expect( double ).toHaveText( '6' ); + await expect( clicks ).toHaveText( '0' ); + + await page.getByTestId( 'counter button' ).click(); + + await expect( value ).toHaveText( '4' ); + await expect( double ).toHaveText( '8' ); + await expect( clicks ).toHaveText( '1' ); + } ); + + test( 'does not break the page when missing', async ( { + interactivityUtils: utils, + page, + } ) => { + const block = 'test/store-tag {"condition":"missing"}'; + await page.goto( utils.getLink( block ) ); + + const clicks = page.getByTestId( 'counter clicks' ); + await expect( clicks ).toHaveText( '0' ); + await page.getByTestId( 'counter button' ).click(); + await expect( clicks ).toHaveText( '1' ); + } ); + + test( 'does not break the page when corrupted', async ( { + interactivityUtils: utils, + page, + } ) => { + const block = 'test/store-tag {"condition":"corrupted-json"}'; + await page.goto( utils.getLink( block ) ); + + const clicks = page.getByTestId( 'counter clicks' ); + await expect( clicks ).toHaveText( '0' ); + await page.getByTestId( 'counter button' ).click(); + await expect( clicks ).toHaveText( '1' ); + } ); + + test( 'does not break the page when it contains an invalid state', async ( { + interactivityUtils: utils, + page, + } ) => { + const block = 'test/store-tag {"condition":"invalid-state"}'; + await page.goto( utils.getLink( block ) ); + + const clicks = page.getByTestId( 'counter clicks' ); + await expect( clicks ).toHaveText( '0' ); + await page.getByTestId( 'counter button' ).click(); + await expect( clicks ).toHaveText( '1' ); + } ); +} ); diff --git a/test/e2e/specs/interactivity/tovdom-islands.spec.ts b/test/e2e/specs/interactivity/tovdom-islands.spec.ts new file mode 100644 index 00000000000000..fcc7c6081077a6 --- /dev/null +++ b/test/e2e/specs/interactivity/tovdom-islands.spec.ts @@ -0,0 +1,58 @@ +/** + * Internal dependencies + */ +import { test, expect } from './fixtures'; + +test.describe( 'toVdom - islands', () => { + test.beforeAll( async ( { interactivityUtils: utils } ) => { + await utils.activatePlugins(); + await utils.addPostWithBlock( 'test/tovdom-islands' ); + } ); + test.beforeEach( async ( { interactivityUtils: utils, page } ) => { + await page.goto( utils.getLink( 'test/tovdom-islands' ) ); + } ); + test.afterAll( async ( { interactivityUtils: utils } ) => { + await utils.deactivatePlugins(); + await utils.deleteAllPosts(); + } ); + + test( 'directives that are not inside islands should not be hydrated', async ( { + page, + } ) => { + const el = page.getByTestId( 'not inside an island' ); + await expect( el ).toBeVisible(); + } ); + + test( 'directives that are inside islands should be hydrated', async ( { + page, + } ) => { + const el = page.getByTestId( 'inside an island' ); + await expect( el ).toBeHidden(); + } ); + + test( 'directives that are inside inner blocks of isolated islands should not be hydrated', async ( { + page, + } ) => { + const el = page.getByTestId( + 'inside an inner block of an isolated island' + ); + await expect( el ).toBeVisible(); + } ); + + test( 'directives inside islands should not be hydrated twice', async ( { + page, + } ) => { + const el = page.getByTestId( 'island inside another island' ); + const templates = el.locator( 'template' ); + expect( await templates.count() ).toEqual( 1 ); + } ); + + test( 'islands inside inner blocks of isolated islands should be hydrated', async ( { + page, + } ) => { + const el = page.getByTestId( + 'island inside inner block of isolated island' + ); + await expect( el ).toBeHidden(); + } ); +} ); diff --git a/test/e2e/specs/interactivity/tovdom.spec.ts b/test/e2e/specs/interactivity/tovdom.spec.ts new file mode 100644 index 00000000000000..cf08fb115db4fd --- /dev/null +++ b/test/e2e/specs/interactivity/tovdom.spec.ts @@ -0,0 +1,55 @@ +/** + * Internal dependencies + */ +import { test, expect } from './fixtures'; + +test.describe( 'toVdom', () => { + test.beforeAll( async ( { interactivityUtils: utils } ) => { + await utils.activatePlugins(); + await utils.addPostWithBlock( 'test/tovdom' ); + } ); + test.beforeEach( async ( { interactivityUtils: utils, page } ) => { + await page.goto( utils.getLink( 'test/tovdom' ) ); + } ); + test.afterAll( async ( { interactivityUtils: utils } ) => { + await utils.deactivatePlugins(); + await utils.deleteAllPosts(); + } ); + + test( 'it should delete comments', async ( { page } ) => { + const el = page.getByTestId( 'it should delete comments' ); + const c = await el.innerHTML(); + expect( c ).not.toContain( '##1##' ); + expect( c ).not.toContain( '##2##' ); + const el2 = page.getByTestId( + 'it should keep this node between comments' + ); + await expect( el2 ).toBeVisible(); + } ); + + test( 'it should delete processing instructions', async ( { page } ) => { + const el = page.getByTestId( + 'it should delete processing instructions' + ); + const c = await el.innerHTML(); + expect( c ).not.toContain( '##1##' ); + expect( c ).not.toContain( '##2##' ); + const el2 = page.getByTestId( + 'it should keep this node between processing instructions' + ); + await expect( el2 ).toBeVisible(); + } ); + + test( 'it should replace CDATA with text nodes', async ( { page } ) => { + const el = page.getByTestId( + 'it should replace CDATA with text nodes' + ); + const c = await el.innerHTML(); + expect( c ).toContain( '##1##' ); + expect( c ).toContain( '##2##' ); + const el2 = page.getByTestId( + 'it should keep this node between CDATA' + ); + await expect( el2 ).toBeVisible(); + } ); +} ); diff --git a/tools/webpack/blocks.js b/tools/webpack/blocks.js index 19ff4e90e214e7..d399ee0d1a34bf 100644 --- a/tools/webpack/blocks.js +++ b/tools/webpack/blocks.js @@ -219,82 +219,4 @@ module.exports = [ } ), ].filter( Boolean ), }, - { - ...baseConfig, - watchOptions: { - aggregateTimeout: 200, - }, - name: 'interactivity', - entry: { - file: './packages/block-library/src/file/interactivity.js', - navigation: - './packages/block-library/src/navigation/interactivity.js', - image: './packages/block-library/src/image/interactivity.js', - }, - output: { - devtoolNamespace: 'wp', - filename: './blocks/[name]/interactivity.min.js', - path: join( __dirname, '..', '..', 'build', 'block-library' ), - }, - optimization: { - ...baseConfig.optimization, - runtimeChunk: { - name: 'vendors', - }, - splitChunks: { - cacheGroups: { - vendors: { - name: 'vendors', - test: /[\\/]node_modules[\\/]/, - filename: './interactivity/[name].min.js', - minSize: 0, - chunks: 'all', - }, - runtime: { - name: 'runtime', - test: /[\\/]utils[\\/]interactivity[\\/]/, - filename: './interactivity/[name].min.js', - chunks: 'all', - minSize: 0, - priority: -10, - }, - }, - }, - }, - module: { - rules: [ - { - test: /\.(j|t)sx?$/, - exclude: /node_modules/, - use: [ - { - loader: require.resolve( 'babel-loader' ), - options: { - cacheDirectory: - process.env.BABEL_CACHE_DIRECTORY || true, - babelrc: false, - configFile: false, - presets: [ - [ - '@babel/preset-react', - { - runtime: 'automatic', - importSource: 'preact', - }, - ], - ], - }, - }, - ], - }, - ], - }, - plugins: [ - ...plugins, - new DependencyExtractionWebpackPlugin( { - __experimentalInjectInteractivityRuntime: true, - injectPolyfill: false, - } ), - ].filter( Boolean ), - }, ]; diff --git a/tools/webpack/interactivity.js b/tools/webpack/interactivity.js new file mode 100644 index 00000000000000..715d824979e5ec --- /dev/null +++ b/tools/webpack/interactivity.js @@ -0,0 +1,56 @@ +/** + * External dependencies + */ +const { join } = require( 'path' ); + +/** + * Internal dependencies + */ +const { baseConfig } = require( './shared' ); + +module.exports = { + ...baseConfig, + name: 'interactivity', + entry: { + index: { + import: `./packages/interactivity/src/index.js`, + library: { + name: [ 'wp', 'interactivity' ], + type: 'window', + }, + }, + }, + output: { + devtoolNamespace: 'wp', + filename: './build/interactivity/[name].min.js', + path: join( __dirname, '..', '..' ), + }, + module: { + rules: [ + { + test: /\.(j|t)sx?$/, + exclude: /node_modules/, + use: [ + { + loader: require.resolve( 'babel-loader' ), + options: { + cacheDirectory: + process.env.BABEL_CACHE_DIRECTORY || true, + babelrc: false, + configFile: false, + presets: [ + [ + '@babel/preset-react', + { + runtime: 'automatic', + importSource: 'preact', + }, + ], + ], + }, + }, + ], + }, + ], + }, +}; diff --git a/tools/webpack/packages.js b/tools/webpack/packages.js index 30f73a82fa0da2..c8016882e41412 100644 --- a/tools/webpack/packages.js +++ b/tools/webpack/packages.js @@ -75,7 +75,8 @@ const gutenbergPackages = Object.keys( dependencies ) ( packageName ) => ! BUNDLED_PACKAGES.includes( packageName ) && packageName.startsWith( WORDPRESS_NAMESPACE ) && - ! packageName.startsWith( WORDPRESS_NAMESPACE + 'react-native' ) + ! packageName.startsWith( WORDPRESS_NAMESPACE + 'react-native' ) && + ! packageName.startsWith( WORDPRESS_NAMESPACE + 'interactivity' ) ) .map( ( packageName ) => packageName.replace( WORDPRESS_NAMESPACE, '' ) ); diff --git a/webpack.config.js b/webpack.config.js index f1c5ce803adc1b..8558707f4bc9fa 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -3,6 +3,12 @@ */ const blocksConfig = require( './tools/webpack/blocks' ); const developmentConfigs = require( './tools/webpack/development' ); +const interactivity = require( './tools/webpack/interactivity' ); const packagesConfig = require( './tools/webpack/packages' ); -module.exports = [ ...blocksConfig, packagesConfig, ...developmentConfigs ]; +module.exports = [ + ...blocksConfig, + interactivity, + packagesConfig, + ...developmentConfigs, +]; From c5d477ab54ad98d3998809ba1e72675eb70f48bc Mon Sep 17 00:00:00 2001 From: Ben Dwyer Date: Wed, 28 Jun 2023 17:17:25 +0100 Subject: [PATCH 068/266] Navigation: Add the draft status to the navigation title (#51967) * Navigation: Add the draft status to the navigation title * Move the buildNavigationLabel function to the default export --- .../index.js | 7 +++++- .../single-navigation-menu.js | 7 +++++- .../build-navigation-label.js | 24 +++++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/build-navigation-label.js 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 92810d1411fc39..5ae860d4bb8298 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 @@ -17,6 +17,7 @@ import { SidebarNavigationScreenWrapper } from '../sidebar-navigation-screen-nav import ScreenNavigationMoreMenu from './more-menu'; import SingleNavigationMenu from './single-navigation-menu'; import useNavigationMenuHandlers from './use-navigation-menu-handlers'; +import buildNavigationLabel from '../sidebar-navigation-screen-navigation-menus/build-navigation-label'; export const postType = `wp_navigation`; @@ -90,7 +91,11 @@ export default function SidebarNavigationScreenNavigationMenu() { onDuplicate={ _handleDuplicate } /> } - title={ decodeEntities( menuTitle ) } + title={ buildNavigationLabel( + navigationMenu?.title, + navigationMenu?.id, + navigationMenu?.status + ) } description={ __( 'This Navigation Menu is empty.' ) } /> ); diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/single-navigation-menu.js b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/single-navigation-menu.js index 6351c83323f98f..d988308b167135 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/single-navigation-menu.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/single-navigation-menu.js @@ -9,6 +9,7 @@ import { decodeEntities } from '@wordpress/html-entities'; import { SidebarNavigationScreenWrapper } from '../sidebar-navigation-screen-navigation-menus'; import ScreenNavigationMoreMenu from './more-menu'; import NavigationMenuEditor from './navigation-menu-editor'; +import buildNavigationLabel from '../sidebar-navigation-screen-navigation-menus/build-navigation-label'; export default function SingleNavigationMenu( { navigationMenu, @@ -28,7 +29,11 @@ export default function SingleNavigationMenu( { onDuplicate={ handleDuplicate } /> } - title={ decodeEntities( menuTitle ) } + title={ buildNavigationLabel( + navigationMenu?.title, + navigationMenu?.id, + navigationMenu?.status + ) } description={ __( 'Navigation menus are a curated collection of blocks that allow visitors to get around your site.' ) } diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/build-navigation-label.js b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/build-navigation-label.js new file mode 100644 index 00000000000000..d5e5ff02c63bfd --- /dev/null +++ b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/build-navigation-label.js @@ -0,0 +1,24 @@ +/** + * WordPress dependencies + */ +import { __, sprintf } from '@wordpress/i18n'; +import { decodeEntities } from '@wordpress/html-entities'; + +// Copied from packages/block-library/src/navigation/edit/navigation-menu-selector.js. +export default function buildNavigationLabel( title, id, status ) { + if ( ! title?.rendered ) { + /* translators: %s is the index of the menu in the list of menus. */ + return sprintf( __( '(no title %s)' ), id ); + } + + if ( status === 'publish' ) { + return decodeEntities( title?.rendered ); + } + + return sprintf( + // translators: %1s: title of the menu; %2s: status of the menu (draft, pending, etc.). + __( '%1$s (%2$s)' ), + decodeEntities( title?.rendered ), + status + ); +} From 56e18f29c176b2a1a3abb51ae6d2ab80f9f831a3 Mon Sep 17 00:00:00 2001 From: James Koster Date: Wed, 28 Jun 2023 20:59:09 +0100 Subject: [PATCH 069/266] Update home template icon (#52075) --- .../edit-site/src/components/add-new-template/new-template.js | 4 ++-- .../src/components/sidebar-navigation-screen-pages/index.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/edit-site/src/components/add-new-template/new-template.js b/packages/edit-site/src/components/add-new-template/new-template.js index 0c6ab03899cc81..31ddd754562dbf 100644 --- a/packages/edit-site/src/components/add-new-template/new-template.js +++ b/packages/edit-site/src/components/add-new-template/new-template.js @@ -34,7 +34,7 @@ import { page, plus, pin, - postList, + verse, search, tag, } from '@wordpress/icons'; @@ -78,7 +78,7 @@ const DEFAULT_TEMPLATE_SLUGS = [ const TEMPLATE_ICONS = { 'front-page': home, - home: postList, + home: verse, single: pin, page, archive, diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-pages/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-pages/index.js index 8cfd041f36d3cc..567bf91aca69fd 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-pages/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-pages/index.js @@ -12,7 +12,7 @@ import { __ } from '@wordpress/i18n'; import { useEntityRecords, store as coreStore } from '@wordpress/core-data'; import { decodeEntities } from '@wordpress/html-entities'; import { privateApis as routerPrivateApis } from '@wordpress/router'; -import { layout, page, home, loop, plus } from '@wordpress/icons'; +import { layout, page, home, verse, plus } from '@wordpress/icons'; import { useSelect } from '@wordpress/data'; /** @@ -159,7 +159,7 @@ export default function SidebarNavigationScreenPages() { itemIcon = home; break; case postsPage: - itemIcon = loop; + itemIcon = verse; break; default: itemIcon = page; From bd2a881101727b03b0be09382b34841af5a3c03e Mon Sep 17 00:00:00 2001 From: Bernie Reiter <96308+ockham@users.noreply.github.com> Date: Wed, 28 Jun 2023 22:59:32 +0200 Subject: [PATCH 070/266] Block Supports: Change prefix in gutenberg_apply_colors_support to wp_ in dynamic blocks (#51989) --- packages/block-library/src/navigation-submenu/index.php | 4 ++-- tools/webpack/blocks.js | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/block-library/src/navigation-submenu/index.php b/packages/block-library/src/navigation-submenu/index.php index 748613193bc709..ee8b0e789dd24c 100644 --- a/packages/block-library/src/navigation-submenu/index.php +++ b/packages/block-library/src/navigation-submenu/index.php @@ -199,9 +199,9 @@ function render_block_core_navigation_submenu( $attributes, $content, $block ) { $attributes['style']['color']['background'] = $block->context['customOverlayBackgroundColor']; } - // This allows us to be able to get a response from gutenberg_apply_colors_support. + // This allows us to be able to get a response from wp_apply_colors_support. $block->block_type->supports['color'] = true; - $colors_supports = gutenberg_apply_colors_support( $block->block_type, $attributes ); + $colors_supports = wp_apply_colors_support( $block->block_type, $attributes ); $css_classes = 'wp-block-navigation__submenu-container'; if ( array_key_exists( 'class', $colors_supports ) ) { $css_classes .= ' ' . $colors_supports['class']; diff --git a/tools/webpack/blocks.js b/tools/webpack/blocks.js index d399ee0d1a34bf..88acc23da59413 100644 --- a/tools/webpack/blocks.js +++ b/tools/webpack/blocks.js @@ -29,6 +29,7 @@ const blockViewRegex = new RegExp( */ const prefixFunctions = [ 'build_query_vars_from_query_block', + 'wp_apply_colors_support', 'wp_enqueue_block_support_styles', 'wp_get_typography_font_size_value', 'wp_style_engine_get_styles', From 95c49482cbb6f342154d7a9a0ea01b402938a6ff Mon Sep 17 00:00:00 2001 From: Ramon Date: Thu, 29 Jun 2023 10:28:40 +1000 Subject: [PATCH 071/266] Revert "Updating social link attributes (#51997)" (#52019) This reverts commit c711e2aa613aceb93836635e854badd8ac15310d. --- packages/block-library/src/social-link/index.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/block-library/src/social-link/index.php b/packages/block-library/src/social-link/index.php index 1e5c4cf5de6fbd..51ed9374c9bcdf 100644 --- a/packages/block-library/src/social-link/index.php +++ b/packages/block-library/src/social-link/index.php @@ -47,8 +47,8 @@ function render_block_core_social_link( $attributes, $content, $block ) { $icon = block_core_social_link_get_icon( $service ); $wrapper_attributes = get_block_wrapper_attributes( array( - 'class' => esc_attr( 'wp-social-link wp-social-link-' . $service . block_core_social_link_get_color_classes( $block->context ) ), - 'style' => esc_attr( block_core_social_link_get_color_styles( $block->context ) ), + 'class' => 'wp-social-link wp-social-link-' . $service . block_core_social_link_get_color_classes( $block->context ), + 'style' => block_core_social_link_get_color_styles( $block->context ), ) ); From 47b60e55a302ab25342ab46bb6fe255ac905241f Mon Sep 17 00:00:00 2001 From: Glen Davies Date: Thu, 29 Jun 2023 14:35:56 +1200 Subject: [PATCH 072/266] Remove ability for user to toggle sync status after pattern creation (#51998) --- .../src/components/post-sync-status/index.js | 29 ++++--------------- .../components/post-sync-status/style.scss | 4 +-- 2 files changed, 8 insertions(+), 25 deletions(-) diff --git a/packages/editor/src/components/post-sync-status/index.js b/packages/editor/src/components/post-sync-status/index.js index 392d33563ef78f..c384fd234c7a34 100644 --- a/packages/editor/src/components/post-sync-status/index.js +++ b/packages/editor/src/components/post-sync-status/index.js @@ -1,9 +1,9 @@ /** * WordPress dependencies */ -import { useSelect, useDispatch } from '@wordpress/data'; +import { useSelect } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; -import { ToggleControl, PanelRow } from '@wordpress/components'; +import { PanelRow } from '@wordpress/components'; /** * Internal dependencies @@ -11,7 +11,6 @@ import { ToggleControl, PanelRow } from '@wordpress/components'; import { store as editorStore } from '../../store'; export default function PostSyncStatus() { - const { editPost } = useDispatch( editorStore ); const { meta, postType } = useSelect( ( select ) => { const { getEditedPostAttribute } = select( editorStore ); return { @@ -22,31 +21,15 @@ export default function PostSyncStatus() { if ( postType !== 'wp_block' ) { return null; } - const onUpdateSync = ( syncStatus ) => - editPost( { - meta: { - ...meta, - sync_status: syncStatus === 'unsynced' ? syncStatus : null, - }, - } ); const syncStatus = meta?.sync_status; const isFullySynced = ! syncStatus; return ( - { __( 'Syncing' ) } - { - onUpdateSync( - syncStatus === 'unsynced' ? 'fully' : 'unsynced' - ); - } } - /> + { __( 'Sync status' ) } +
+ { isFullySynced ? __( 'Fully synced' ) : __( 'Not synced' ) } +
); } diff --git a/packages/editor/src/components/post-sync-status/style.scss b/packages/editor/src/components/post-sync-status/style.scss index 385577b3334d86..7f81a327443ee1 100644 --- a/packages/editor/src/components/post-sync-status/style.scss +++ b/packages/editor/src/components/post-sync-status/style.scss @@ -9,8 +9,8 @@ flex-shrink: 0; } - .components-base-control { + > div { // Match padding on tertiary buttons for alignment. - padding-left: $grid-unit-15 * 0.5; + padding-left: $grid-unit-15; } } From 3e93697852e5884d4d8de5e3ed04970ad80d3438 Mon Sep 17 00:00:00 2001 From: Carolina Nymark Date: Thu, 29 Jun 2023 05:45:20 +0200 Subject: [PATCH 073/266] Focus Mode: Use the symbol icon if a pattern is being edited (#52031) --- .../header-edit-mode/document-actions/index.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/edit-site/src/components/header-edit-mode/document-actions/index.js b/packages/edit-site/src/components/header-edit-mode/document-actions/index.js index f31c789043e72e..3a048e753631f0 100644 --- a/packages/edit-site/src/components/header-edit-mode/document-actions/index.js +++ b/packages/edit-site/src/components/header-edit-mode/document-actions/index.js @@ -20,6 +20,7 @@ import { chevronLeftSmall as chevronLeftSmallIcon, page as pageIcon, navigation as navigationIcon, + symbol, } from '@wordpress/icons'; import { displayShortcut } from '@wordpress/keycodes'; import { useState, useEffect, useRef } from '@wordpress/element'; @@ -118,10 +119,17 @@ function TemplateDocumentActions( { className, onBack } ) { const entityLabel = getEntityLabel( record.type ); + let typeIcon = icon; + if ( record.type === 'wp_navigation' ) { + typeIcon = navigationIcon; + } else if ( record.type === 'wp_block' ) { + typeIcon = symbol; + } + return ( From dc1dab940088f3f6c1e7aa7c529f7441258549dd Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Thu, 29 Jun 2023 16:04:06 +1000 Subject: [PATCH 074/266] Move block editor settings filter into 6.3 compat folder (#52100) --- .../wordpress-6.3}/block-editor-settings.php | 0 lib/load.php | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename lib/{experimental => compat/wordpress-6.3}/block-editor-settings.php (100%) diff --git a/lib/experimental/block-editor-settings.php b/lib/compat/wordpress-6.3/block-editor-settings.php similarity index 100% rename from lib/experimental/block-editor-settings.php rename to lib/compat/wordpress-6.3/block-editor-settings.php diff --git a/lib/load.php b/lib/load.php index 25ee57c205f313..609fee77240e4a 100644 --- a/lib/load.php +++ b/lib/load.php @@ -97,12 +97,12 @@ function gutenberg_is_experiment_enabled( $name ) { require __DIR__ . '/compat/wordpress-6.3/script-loader.php'; require __DIR__ . '/compat/wordpress-6.3/blocks.php'; require __DIR__ . '/compat/wordpress-6.3/navigation-fallback.php'; +require __DIR__ . '/compat/wordpress-6.3/block-editor-settings.php'; // Experimental features. remove_action( 'plugins_loaded', '_wp_theme_json_webfonts_handler' ); // Turns off WP 6.0's stopgap handler for Webfonts API. require __DIR__ . '/experimental/behaviors.php'; require __DIR__ . '/experimental/block-editor-settings-mobile.php'; -require __DIR__ . '/experimental/block-editor-settings.php'; require __DIR__ . '/experimental/blocks.php'; require __DIR__ . '/experimental/navigation-theme-opt-in.php'; require __DIR__ . '/experimental/kses.php'; From 14e1c6f04f6070a09c3cd92372d7c417f39d45b0 Mon Sep 17 00:00:00 2001 From: Ella <4710635+ellatrix@users.noreply.github.com> Date: Thu, 29 Jun 2023 10:05:33 +0300 Subject: [PATCH 075/266] Footnotes: register meta field for pages (#52024) --- .../block-library/src/footnotes/index.php | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/block-library/src/footnotes/index.php b/packages/block-library/src/footnotes/index.php index b7be0542d7a0f3..e4bd8dcdeaac2e 100644 --- a/packages/block-library/src/footnotes/index.php +++ b/packages/block-library/src/footnotes/index.php @@ -59,15 +59,17 @@ function render_block_core_footnotes( $attributes, $content, $block ) { * Registers the `core/footnotes` block on the server. */ function register_block_core_footnotes() { - register_post_meta( - 'post', - 'footnotes', - array( - 'show_in_rest' => true, - 'single' => true, - 'type' => 'string', - ) - ); + foreach ( array( 'post', 'page' ) as $post_type ) { + register_post_meta( + $post_type, + 'footnotes', + array( + 'show_in_rest' => true, + 'single' => true, + 'type' => 'string', + ) + ); + } register_block_type_from_metadata( __DIR__ . '/footnotes', array( From 76e03d7e2f9eebcfcef44011ceaaac5812cde56a Mon Sep 17 00:00:00 2001 From: Jonny Harris Date: Thu, 29 Jun 2023 09:06:55 +0100 Subject: [PATCH 076/266] Add caching to schema of REST API. (#52045) --- ...gutenberg-rest-block-pattern-categories-controller.php | 8 +++++++- ...class-gutenberg-rest-block-patterns-controller-6-2.php | 8 +++++++- ...class-gutenberg-rest-block-patterns-controller-6-3.php | 8 +++++++- .../class-wp-rest-block-editor-settings-controller.php | 4 +++- 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/lib/compat/wordpress-6.2/class-gutenberg-rest-block-pattern-categories-controller.php b/lib/compat/wordpress-6.2/class-gutenberg-rest-block-pattern-categories-controller.php index 970b1c4a1a330d..3897a8945a5f95 100644 --- a/lib/compat/wordpress-6.2/class-gutenberg-rest-block-pattern-categories-controller.php +++ b/lib/compat/wordpress-6.2/class-gutenberg-rest-block-pattern-categories-controller.php @@ -47,6 +47,10 @@ public function prepare_item_for_response( $item, $request ) { * @return array Item schema data. */ public function get_item_schema() { + if ( $this->schema ) { + return $this->add_additional_fields_schema( $this->schema ); + } + $schema = array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'block-pattern-category', @@ -73,6 +77,8 @@ public function get_item_schema() { ), ); - return $this->add_additional_fields_schema( $schema ); + $this->schema = $schema; + + return $this->add_additional_fields_schema( $this->schema ); } } diff --git a/lib/compat/wordpress-6.2/class-gutenberg-rest-block-patterns-controller-6-2.php b/lib/compat/wordpress-6.2/class-gutenberg-rest-block-patterns-controller-6-2.php index 70a9ae397fe8d6..80de88aa31a816 100644 --- a/lib/compat/wordpress-6.2/class-gutenberg-rest-block-patterns-controller-6-2.php +++ b/lib/compat/wordpress-6.2/class-gutenberg-rest-block-patterns-controller-6-2.php @@ -79,6 +79,10 @@ public function prepare_item_for_response( $item, $request ) { * @return array Item schema data. */ public function get_item_schema() { + if ( $this->schema ) { + return $this->add_additional_fields_schema( $this->schema ); + } + $schema = array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'block-pattern', @@ -153,7 +157,9 @@ public function get_item_schema() { ), ); - return $this->add_additional_fields_schema( $schema ); + $this->schema = $schema; + + return $this->add_additional_fields_schema( $this->schema ); } /** diff --git a/lib/compat/wordpress-6.3/class-gutenberg-rest-block-patterns-controller-6-3.php b/lib/compat/wordpress-6.3/class-gutenberg-rest-block-patterns-controller-6-3.php index 036864bd8190fe..ac40e6b842f523 100644 --- a/lib/compat/wordpress-6.3/class-gutenberg-rest-block-patterns-controller-6-3.php +++ b/lib/compat/wordpress-6.3/class-gutenberg-rest-block-patterns-controller-6-3.php @@ -62,6 +62,10 @@ public function prepare_item_for_response( $item, $request ) { * @return array Item schema data. */ public function get_item_schema() { + if ( $this->schema ) { + return $this->add_additional_fields_schema( $this->schema ); + } + $schema = array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'block-pattern', @@ -150,6 +154,8 @@ public function get_item_schema() { ), ); - return $this->add_additional_fields_schema( $schema ); + $this->schema = $schema; + + return $this->add_additional_fields_schema( $this->schema ); } } diff --git a/lib/experimental/class-wp-rest-block-editor-settings-controller.php b/lib/experimental/class-wp-rest-block-editor-settings-controller.php index 7bca2a9e8e9967..52dd328fc7f2db 100644 --- a/lib/experimental/class-wp-rest-block-editor-settings-controller.php +++ b/lib/experimental/class-wp-rest-block-editor-settings-controller.php @@ -109,7 +109,7 @@ public function get_item_schema() { return $this->add_additional_fields_schema( $this->schema ); } - $this->schema = array( + $schema = array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'block-editor-settings-item', 'type' => 'object', @@ -311,6 +311,8 @@ public function get_item_schema() { ), ); + $this->schema = $schema; + return $this->add_additional_fields_schema( $this->schema ); } } From 3b7902e6885fd12c660e4cfd94f115ebfbe91434 Mon Sep 17 00:00:00 2001 From: Andrei Draganescu Date: Thu, 29 Jun 2023 11:34:08 +0300 Subject: [PATCH 077/266] Fix unintentional toggling on of distraction free (#52090) * replace toggle with set preference - because I don't read code properly it seems * remove notification --- .../index.js | 44 ++++++++++++------- 1 file changed, 29 insertions(+), 15 deletions(-) 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 c10de840e8d478..06278590696cbc 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 @@ -16,6 +16,7 @@ import { BlockEditorProvider } from '@wordpress/block-editor'; import { humanTimeDiff } from '@wordpress/date'; import { useCallback } from '@wordpress/element'; import { store as noticesStore } from '@wordpress/notices'; +import { store as preferencesStore } from '@wordpress/preferences'; /** * Internal dependencies @@ -32,14 +33,21 @@ import useGlobalStylesRevisions from '../global-styles/screen-revisions/use-glob const noop = () => {}; export function SidebarNavigationItemGlobalStyles( props ) { - const { openGeneralSidebar, toggleFeature } = useDispatch( editSiteStore ); + const { openGeneralSidebar } = useDispatch( editSiteStore ); const { setCanvasMode } = unlock( useDispatch( editSiteStore ) ); const { createNotice } = useDispatch( noticesStore ); - const hasGlobalStyleVariations = useSelect( - ( select ) => - !! select( - coreStore - ).__experimentalGetCurrentThemeGlobalStylesVariations()?.length, + const { set: setPreference } = useDispatch( preferencesStore ); + const { hasGlobalStyleVariations, isDistractionFree } = useSelect( + ( select ) => ( { + hasGlobalStyleVariations: + !! select( + coreStore + ).__experimentalGetCurrentThemeGlobalStylesVariations()?.length, + isDistractionFree: select( preferencesStore ).get( + editSiteStore.name, + 'distractionFree' + ), + } ), [] ); if ( hasGlobalStyleVariations ) { @@ -56,15 +64,21 @@ export function SidebarNavigationItemGlobalStyles( props ) { { ...props } onClick={ () => { // Disable distraction free mode. - toggleFeature( 'distractionFree', false ); - createNotice( - 'info', - __( 'Distraction free mode turned off' ), - { - isDismissible: true, - type: 'snackbar', - } - ); + if ( isDistractionFree ) { + setPreference( + editSiteStore.name, + 'distractionFree', + false + ); + createNotice( + 'info', + __( 'Distraction free mode turned off' ), + { + isDismissible: true, + type: 'snackbar', + } + ); + } // Switch to edit mode. setCanvasMode( 'edit' ); // Open global styles sidebar. From 55c18fc594445f9c312b851ab707daa504abd6f7 Mon Sep 17 00:00:00 2001 From: Miguel Fonseca <150562+mcsf@users.noreply.github.com> Date: Thu, 29 Jun 2023 11:05:00 +0100 Subject: [PATCH 078/266] Block editor store: also attach private APIs to old store descriptor (#52088) As a workaround, until #39632 is merged, make sure that private actions and selectors can be unlocked from the original store descriptor (the one created by `createReduxStore`) and not just the one registered in the default registry (created by `registerStore`). Without this workaround, specific setups will unexpectedly fail, such as the action tests in the `reusable-blocks` package, due to the way that those tests create their own registries in which they register stores like `block-editor`. Context: https://github.com/WordPress/gutenberg/pull/51145#discussion_r1239999590 Props jsnajdr --- packages/block-editor/src/store/index.js | 10 ++++++++++ packages/block-editor/src/store/private-actions.js | 14 +------------- packages/block-editor/src/store/test/actions.js | 3 +++ 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/packages/block-editor/src/store/index.js b/packages/block-editor/src/store/index.js index ed17b387ba5884..0bcc00cb5f6ae8 100644 --- a/packages/block-editor/src/store/index.js +++ b/packages/block-editor/src/store/index.js @@ -43,3 +43,13 @@ const registeredStore = registerStore( STORE_NAME, { } ); unlock( registeredStore ).registerPrivateActions( privateActions ); unlock( registeredStore ).registerPrivateSelectors( privateSelectors ); + +// TODO: Remove once we switch to the `register` function (see above). +// +// Until then, private functions also need to be attached to the original +// `store` descriptor in order to avoid unit tests failing, which could happen +// when tests create new registries in which they register stores. +// +// @see https://github.com/WordPress/gutenberg/pull/51145#discussion_r1239999590 +unlock( store ).registerPrivateActions( privateActions ); +unlock( store ).registerPrivateSelectors( privateSelectors ); diff --git a/packages/block-editor/src/store/private-actions.js b/packages/block-editor/src/store/private-actions.js index 8b5e066e5a83c0..d90aae340a92a7 100644 --- a/packages/block-editor/src/store/private-actions.js +++ b/packages/block-editor/src/store/private-actions.js @@ -161,19 +161,7 @@ export const privateRemoveBlocks = // register using `toggleRemovalPromptSupport()`. // // @see https://github.com/WordPress/gutenberg/pull/51145 - if ( - ! forceRemove && - // FIXME: Without this existence check, the unit tests for - // `__experimentalDeleteReusableBlock` in - // `packages/reusable-blocks/src/store/test/actions.js` fail due to - // the fact that the `registry` object passed to the thunk actions - // doesn't include this private action. This needs to be - // investigated to understand whether it's a real smell or if it's - // because not all store code has been updated to accommodate - // private selectors. - select.isRemovalPromptSupported && - select.isRemovalPromptSupported() - ) { + if ( ! forceRemove && select.isRemovalPromptSupported() ) { const blockNamesForPrompt = new Set(); // Given a list of client IDs of blocks that the user intended to diff --git a/packages/block-editor/src/store/test/actions.js b/packages/block-editor/src/store/test/actions.js index 27cd77b0769931..78df4a3e131d70 100644 --- a/packages/block-editor/src/store/test/actions.js +++ b/packages/block-editor/src/store/test/actions.js @@ -618,6 +618,7 @@ describe( 'actions', () => { const select = { getBlockRootClientId: () => undefined, canRemoveBlocks: () => true, + isRemovalPromptSupported: () => false, }; const dispatch = Object.assign( jest.fn(), { selectPreviousBlock: jest.fn(), @@ -728,6 +729,7 @@ describe( 'actions', () => { const select = { getBlockRootClientId: () => null, canRemoveBlocks: () => true, + isRemovalPromptSupported: () => false, }; const dispatch = Object.assign( jest.fn(), { selectPreviousBlock: jest.fn(), @@ -752,6 +754,7 @@ describe( 'actions', () => { const select = { getBlockRootClientId: () => null, canRemoveBlocks: () => true, + isRemovalPromptSupported: () => false, }; const dispatch = Object.assign( jest.fn(), { selectPreviousBlock: jest.fn(), From 3633d94e08acacf7259f39bd5595b21548e5e924 Mon Sep 17 00:00:00 2001 From: Dave Smith Date: Thu, 29 Jun 2023 11:35:35 +0100 Subject: [PATCH 079/266] Fix fetching Nav fallback ID flushing Navigation entity cache (#52069) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Only flush the `getEntityRecords` cache if the fallback isn’t already in state * Save the edited entity record to a const and then invert it to determine whether we should invalidate the recordds --------- Co-authored-by: scruffian --- packages/core-data/src/resolvers.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/core-data/src/resolvers.js b/packages/core-data/src/resolvers.js index 32baa691ddd822..93dcfd6ee9014f 100644 --- a/packages/core-data/src/resolvers.js +++ b/packages/core-data/src/resolvers.js @@ -573,7 +573,7 @@ export const getBlockPatternCategories = export const getNavigationFallbackId = () => - async ( { dispatch } ) => { + async ( { dispatch, select } ) => { const fallback = await apiFetch( { path: addQueryArgs( '/wp-block-editor/v1/navigation-fallback', { _embed: true, @@ -585,8 +585,15 @@ export const getNavigationFallbackId = dispatch.receiveNavigationFallbackId( fallback?.id ); if ( record ) { - const invalidateNavigationQueries = true; - + // If the fallback is already in the store, don't invalidate navigation queries. + // Otherwise, invalidate the cache for the scenario where there were no Navigation + // posts in the state and the fallback created one. + const existingFallbackEntityRecord = select.getEntityRecord( + 'postType', + 'wp_navigation', + fallback?.id + ); + const invalidateNavigationQueries = ! existingFallbackEntityRecord; dispatch.receiveEntityRecords( 'postType', 'wp_navigation', From 3c3247040f2bd1de80ec34ed044f9811010d0e33 Mon Sep 17 00:00:00 2001 From: Alex Lende Date: Thu, 29 Jun 2023 05:57:39 -0500 Subject: [PATCH 080/266] Refactor, document, and fix image block deprecations (#52081) * Refactor, document, and fix image block deprecations * Fix v5 attributes & supports * Fix v1 & v2 deprecation tests * Rename deprecated test descriptions --- .../block-library/src/image/deprecated.js | 818 +++++++++++------- ...__deprecated-v1-add-responsive-class.html} | 0 ...__deprecated-v1-add-responsive-class.json} | 2 +- ...cated-v1-add-responsive-class.parsed.json} | 0 ...d-v1-add-responsive-class.serialized.html} | 0 ...__deprecated-v2-add-is-resized-class.html} | 0 ...__deprecated-v2-add-is-resized-class.json} | 2 +- ...cated-v2-add-is-resized-class.parsed.json} | 0 ...d-v2-add-is-resized-class.serialized.html} | 0 ...age__deprecated-v3-add-align-wrapper.html} | 0 ...age__deprecated-v3-add-align-wrapper.json} | 0 ...precated-v3-add-align-wrapper.parsed.json} | 0 ...ated-v3-add-align-wrapper.serialized.html} | 0 ...__deprecated-v4-remove-align-wrapper.html} | 0 ...__deprecated-v4-remove-align-wrapper.json} | 0 ...cated-v4-remove-align-wrapper.parsed.json} | 0 ...d-v4-remove-align-wrapper.serialized.html} | 0 ...precated-v5-move-border-radius-style.html} | 0 ...precated-v5-move-border-radius-style.json} | 0 ...d-v5-move-border-radius-style.parsed.json} | 0 ...-move-border-radius-style.serialized.html} | 0 21 files changed, 502 insertions(+), 320 deletions(-) rename test/integration/fixtures/blocks/{core__image__deprecated-1.html => core__image__deprecated-v1-add-responsive-class.html} (100%) rename test/integration/fixtures/blocks/{core__image__deprecated-1.json => core__image__deprecated-v1-add-responsive-class.json} (93%) rename test/integration/fixtures/blocks/{core__image__deprecated-1.parsed.json => core__image__deprecated-v1-add-responsive-class.parsed.json} (100%) rename test/integration/fixtures/blocks/{core__image__deprecated-1.serialized.html => core__image__deprecated-v1-add-responsive-class.serialized.html} (100%) rename test/integration/fixtures/blocks/{core__image__deprecated-2.html => core__image__deprecated-v2-add-is-resized-class.html} (100%) rename test/integration/fixtures/blocks/{core__image__deprecated-3.json => core__image__deprecated-v2-add-is-resized-class.json} (94%) rename test/integration/fixtures/blocks/{core__image__deprecated-2.parsed.json => core__image__deprecated-v2-add-is-resized-class.parsed.json} (100%) rename test/integration/fixtures/blocks/{core__image__deprecated-2.serialized.html => core__image__deprecated-v2-add-is-resized-class.serialized.html} (100%) rename test/integration/fixtures/blocks/{core__image__deprecated-3.html => core__image__deprecated-v3-add-align-wrapper.html} (100%) rename test/integration/fixtures/blocks/{core__image__deprecated-2.json => core__image__deprecated-v3-add-align-wrapper.json} (100%) rename test/integration/fixtures/blocks/{core__image__deprecated-3.parsed.json => core__image__deprecated-v3-add-align-wrapper.parsed.json} (100%) rename test/integration/fixtures/blocks/{core__image__deprecated-3.serialized.html => core__image__deprecated-v3-add-align-wrapper.serialized.html} (100%) rename test/integration/fixtures/blocks/{core__image__deprecated-align-wrapper.html => core__image__deprecated-v4-remove-align-wrapper.html} (100%) rename test/integration/fixtures/blocks/{core__image__deprecated-align-wrapper.json => core__image__deprecated-v4-remove-align-wrapper.json} (100%) rename test/integration/fixtures/blocks/{core__image__deprecated-align-wrapper.parsed.json => core__image__deprecated-v4-remove-align-wrapper.parsed.json} (100%) rename test/integration/fixtures/blocks/{core__image__deprecated-align-wrapper.serialized.html => core__image__deprecated-v4-remove-align-wrapper.serialized.html} (100%) rename test/integration/fixtures/blocks/{core__image__deprecated-border-radius-5.html => core__image__deprecated-v5-move-border-radius-style.html} (100%) rename test/integration/fixtures/blocks/{core__image__deprecated-border-radius-5.json => core__image__deprecated-v5-move-border-radius-style.json} (100%) rename test/integration/fixtures/blocks/{core__image__deprecated-border-radius-5.parsed.json => core__image__deprecated-v5-move-border-radius-style.parsed.json} (100%) rename test/integration/fixtures/blocks/{core__image__deprecated-border-radius-5.serialized.html => core__image__deprecated-v5-move-border-radius-style.serialized.html} (100%) diff --git a/packages/block-library/src/image/deprecated.js b/packages/block-library/src/image/deprecated.js index 9b7a41cab188de..34b4573c1002cb 100644 --- a/packages/block-library/src/image/deprecated.js +++ b/packages/block-library/src/image/deprecated.js @@ -8,353 +8,535 @@ import classnames from 'classnames'; */ import { RichText, useBlockProps } from '@wordpress/block-editor'; -const blockAttributes = { - align: { - type: 'string', - }, - url: { - type: 'string', - source: 'attribute', - selector: 'img', - attribute: 'src', - }, - alt: { - type: 'string', - source: 'attribute', - selector: 'img', - attribute: 'alt', - default: '', - }, - caption: { - type: 'string', - source: 'html', - selector: 'figcaption', - }, - title: { - type: 'string', - source: 'attribute', - selector: 'img', - attribute: 'title', - }, - href: { - type: 'string', - source: 'attribute', - selector: 'figure > a', - attribute: 'href', - }, - rel: { - type: 'string', - source: 'attribute', - selector: 'figure > a', - attribute: 'rel', - }, - linkClass: { - type: 'string', - source: 'attribute', - selector: 'figure > a', - attribute: 'class', - }, - id: { - type: 'number', - }, - width: { - type: 'number', - }, - height: { - type: 'number', - }, - sizeSlug: { - type: 'string', - }, - linkDestination: { - type: 'string', +/** + * Deprecation for adding the `wp-image-${id}` class to the image block for + * responsive images. + * + * @see https://github.com/WordPress/gutenberg/pull/4898 + */ +const v1 = { + attributes: { + url: { + type: 'string', + source: 'attribute', + selector: 'img', + attribute: 'src', + }, + alt: { + type: 'string', + source: 'attribute', + selector: 'img', + attribute: 'alt', + default: '', + }, + caption: { + type: 'array', + source: 'children', + selector: 'figcaption', + }, + href: { + type: 'string', + source: 'attribute', + selector: 'a', + attribute: 'href', + }, + id: { + type: 'number', + }, + align: { + type: 'string', + }, + width: { + type: 'number', + }, + height: { + type: 'number', + }, }, - linkTarget: { - type: 'string', - source: 'attribute', - selector: 'figure > a', - attribute: 'target', + save( { attributes } ) { + const { url, alt, caption, align, href, width, height } = attributes; + const extraImageProps = width || height ? { width, height } : {}; + const image = {; + + let figureStyle = {}; + + if ( width ) { + figureStyle = { width }; + } else if ( align === 'left' || align === 'right' ) { + figureStyle = { maxWidth: '50%' }; + } + + return ( +
+ { href ? { image } : image } + { ! RichText.isEmpty( caption ) && ( + + ) } +
+ ); }, }; -const blockSupports = { - anchor: true, - color: { - __experimentalDuotone: 'img', - text: false, - background: false, - }, - __experimentalBorder: { - radius: true, - __experimentalDefaultControls: { - radius: true, +/** + * Deprecation for adding the `is-resized` class to the image block to fix + * captions on resized images. + * + * @see https://github.com/WordPress/gutenberg/pull/6496 + */ +const v2 = { + attributes: { + url: { + type: 'string', + source: 'attribute', + selector: 'img', + attribute: 'src', + }, + alt: { + type: 'string', + source: 'attribute', + selector: 'img', + attribute: 'alt', + default: '', + }, + caption: { + type: 'array', + source: 'children', + selector: 'figcaption', + }, + href: { + type: 'string', + source: 'attribute', + selector: 'a', + attribute: 'href', + }, + id: { + type: 'number', + }, + align: { + type: 'string', + }, + width: { + type: 'number', + }, + height: { + type: 'number', }, }, -}; + save( { attributes } ) { + const { url, alt, caption, align, href, width, height, id } = + attributes; -const deprecated = [ - // The following deprecation moves existing border radius styles onto the - // inner img element where new border block support styles must be applied. - // It will also add a new `.has-custom-border` class for existing blocks - // with border radii set. This class is required to improve caption position - // and styling when an image within a gallery has a custom border or - // rounded corners. - // - // See: https://github.com/WordPress/gutenberg/pull/31366/ - { - attributes: blockAttributes, - supports: blockSupports, - save( { attributes } ) { - const { - url, - alt, - caption, - align, - href, - rel, - linkClass, - width, - height, - id, - linkTarget, - sizeSlug, - title, - } = attributes; + const image = ( + { + ); - const newRel = ! rel ? undefined : rel; + return ( +
+ { href ? { image } : image } + { ! RichText.isEmpty( caption ) && ( + + ) } +
+ ); + }, +}; - const classes = classnames( { - [ `align${ align }` ]: align, - [ `size-${ sizeSlug }` ]: sizeSlug, - 'is-resized': width || height, - } ); +/** + * Deprecation for image floats including a wrapping div. + * + * @see https://github.com/WordPress/gutenberg/pull/7721 + */ +const v3 = { + attributes: { + url: { + type: 'string', + source: 'attribute', + selector: 'img', + attribute: 'src', + }, + alt: { + type: 'string', + source: 'attribute', + selector: 'img', + attribute: 'alt', + default: '', + }, + caption: { + type: 'array', + source: 'children', + selector: 'figcaption', + }, + href: { + type: 'string', + source: 'attribute', + selector: 'figure > a', + attribute: 'href', + }, + id: { + type: 'number', + }, + align: { + type: 'string', + }, + width: { + type: 'number', + }, + height: { + type: 'number', + }, + linkDestination: { + type: 'string', + default: 'none', + }, + }, + save( { attributes } ) { + const { url, alt, caption, align, href, width, height, id } = + attributes; - const image = ( - { - ); + const classes = classnames( { + [ `align${ align }` ]: align, + 'is-resized': width || height, + } ); - const figure = ( - <> - { href ? ( - - { image } - - ) : ( - image - ) } - { ! RichText.isEmpty( caption ) && ( - - ) } - - ); + const image = ( + { + ); - return ( -
- { figure } -
- ); - }, + return ( +
+ { href ? { image } : image } + { ! RichText.isEmpty( caption ) && ( + + ) } +
+ ); }, - { - attributes: { - ...blockAttributes, - title: { - type: 'string', - source: 'attribute', - selector: 'img', - attribute: 'title', - }, - sizeSlug: { - type: 'string', - }, - }, - supports: blockSupports, - save( { attributes } ) { - const { - url, - alt, - caption, - align, - href, - rel, - linkClass, - width, - height, - id, - linkTarget, - sizeSlug, - title, - } = attributes; +}; - const newRel = ! rel ? undefined : rel; +/** + * Deprecation for removing the outer div wrapper around aligned images. + * + * @see https://github.com/WordPress/gutenberg/pull/38657 + */ +const v4 = { + attributes: { + align: { + type: 'string', + }, + url: { + type: 'string', + source: 'attribute', + selector: 'img', + attribute: 'src', + }, + alt: { + type: 'string', + source: 'attribute', + selector: 'img', + attribute: 'alt', + default: '', + }, + caption: { + type: 'string', + source: 'html', + selector: 'figcaption', + }, + title: { + type: 'string', + source: 'attribute', + selector: 'img', + attribute: 'title', + }, + href: { + type: 'string', + source: 'attribute', + selector: 'figure > a', + attribute: 'href', + }, + rel: { + type: 'string', + source: 'attribute', + selector: 'figure > a', + attribute: 'rel', + }, + linkClass: { + type: 'string', + source: 'attribute', + selector: 'figure > a', + attribute: 'class', + }, + id: { + type: 'number', + }, + width: { + type: 'number', + }, + height: { + type: 'number', + }, + sizeSlug: { + type: 'string', + }, + linkDestination: { + type: 'string', + }, + linkTarget: { + type: 'string', + source: 'attribute', + selector: 'figure > a', + attribute: 'target', + }, + }, + supports: { + anchor: true, + }, + save( { attributes } ) { + const { + url, + alt, + caption, + align, + href, + rel, + linkClass, + width, + height, + id, + linkTarget, + sizeSlug, + title, + } = attributes; - const classes = classnames( { - [ `align${ align }` ]: align, - [ `size-${ sizeSlug }` ]: sizeSlug, - 'is-resized': width || height, - } ); + const newRel = ! rel ? undefined : rel; - const image = ( - { - ); + const classes = classnames( { + [ `align${ align }` ]: align, + [ `size-${ sizeSlug }` ]: sizeSlug, + 'is-resized': width || height, + } ); - const figure = ( - <> - { href ? ( - - { image } - - ) : ( - image - ) } - { ! RichText.isEmpty( caption ) && ( - - ) } - - ); + const image = ( + { + ); - if ( 'left' === align || 'right' === align || 'center' === align ) { - return ( -
-
{ figure }
-
- ); - } + const figure = ( + <> + { href ? ( + + { image } + + ) : ( + image + ) } + { ! RichText.isEmpty( caption ) && ( + + ) } + + ); + if ( 'left' === align || 'right' === align || 'center' === align ) { return ( -
- { figure } -
+
+
{ figure }
+
); - }, - }, - { - attributes: blockAttributes, - save( { attributes } ) { - const { url, alt, caption, align, href, width, height, id } = - attributes; - - const classes = classnames( { - [ `align${ align }` ]: align, - 'is-resized': width || height, - } ); + } - const image = ( - { - ); + return ( +
+ { figure } +
+ ); + }, +}; - return ( -
- { href ? { image } : image } - { ! RichText.isEmpty( caption ) && ( - - ) } -
- ); +/** + * Deprecation for moving existing border radius styles onto the inner img + * element where new border block support styles must be applied. + * It will also add a new `.has-custom-border` class for existing blocks + * with border radii set. This class is required to improve caption position + * and styling when an image within a gallery has a custom border or + * rounded corners. + * + * @see https://github.com/WordPress/gutenberg/pull/31366 + */ +const v5 = { + attributes: { + align: { + type: 'string', + }, + url: { + type: 'string', + source: 'attribute', + selector: 'img', + attribute: 'src', + }, + alt: { + type: 'string', + source: 'attribute', + selector: 'img', + attribute: 'alt', + default: '', + }, + caption: { + type: 'string', + source: 'html', + selector: 'figcaption', + }, + title: { + type: 'string', + source: 'attribute', + selector: 'img', + attribute: 'title', + }, + href: { + type: 'string', + source: 'attribute', + selector: 'figure > a', + attribute: 'href', + }, + rel: { + type: 'string', + source: 'attribute', + selector: 'figure > a', + attribute: 'rel', + }, + linkClass: { + type: 'string', + source: 'attribute', + selector: 'figure > a', + attribute: 'class', + }, + id: { + type: 'number', + }, + width: { + type: 'number', + }, + height: { + type: 'number', + }, + sizeSlug: { + type: 'string', + }, + linkDestination: { + type: 'string', + }, + linkTarget: { + type: 'string', + source: 'attribute', + selector: 'figure > a', + attribute: 'target', }, }, - { - attributes: blockAttributes, - save( { attributes } ) { - const { url, alt, caption, align, href, width, height, id } = - attributes; - - const image = ( - { - ); - - return ( -
- { href ? { image } : image } - { ! RichText.isEmpty( caption ) && ( - - ) } -
- ); + supports: { + anchor: true, + color: { + __experimentalDuotone: 'img', + text: false, + background: false, + }, + __experimentalBorder: { + radius: true, + __experimentalDefaultControls: { + radius: true, + }, + }, + __experimentalStyle: { + spacing: { + margin: '0 0 1em 0', + }, }, }, - { - attributes: blockAttributes, - save( { attributes } ) { - const { url, alt, caption, align, href, width, height } = - attributes; - const extraImageProps = width || height ? { width, height } : {}; - const image = ( - { - ); + save( { attributes } ) { + const { + url, + alt, + caption, + align, + href, + rel, + linkClass, + width, + height, + id, + linkTarget, + sizeSlug, + title, + } = attributes; - let figureStyle = {}; + const newRel = ! rel ? undefined : rel; - if ( width ) { - figureStyle = { width }; - } else if ( align === 'left' || align === 'right' ) { - figureStyle = { maxWidth: '50%' }; - } + const classes = classnames( { + [ `align${ align }` ]: align, + [ `size-${ sizeSlug }` ]: sizeSlug, + 'is-resized': width || height, + } ); - return ( -
- { href ? { image } : image } - { ! RichText.isEmpty( caption ) && ( - - ) } -
- ); - }, + const image = ( + { + ); + + const figure = ( + <> + { href ? ( + + { image } + + ) : ( + image + ) } + { ! RichText.isEmpty( caption ) && ( + + ) } + + ); + + return ( +
+ { figure } +
+ ); }, -]; +}; -export default deprecated; +export default [ v5, v4, v3, v2, v1 ]; diff --git a/test/integration/fixtures/blocks/core__image__deprecated-1.html b/test/integration/fixtures/blocks/core__image__deprecated-v1-add-responsive-class.html similarity index 100% rename from test/integration/fixtures/blocks/core__image__deprecated-1.html rename to test/integration/fixtures/blocks/core__image__deprecated-v1-add-responsive-class.html diff --git a/test/integration/fixtures/blocks/core__image__deprecated-1.json b/test/integration/fixtures/blocks/core__image__deprecated-v1-add-responsive-class.json similarity index 93% rename from test/integration/fixtures/blocks/core__image__deprecated-1.json rename to test/integration/fixtures/blocks/core__image__deprecated-v1-add-responsive-class.json index e5d1dcba576aaf..24be125d856abf 100644 --- a/test/integration/fixtures/blocks/core__image__deprecated-1.json +++ b/test/integration/fixtures/blocks/core__image__deprecated-v1-add-responsive-class.json @@ -6,7 +6,7 @@ "align": "left", "url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==", "alt": "", - "caption": "" + "caption": [] }, "innerBlocks": [] } diff --git a/test/integration/fixtures/blocks/core__image__deprecated-1.parsed.json b/test/integration/fixtures/blocks/core__image__deprecated-v1-add-responsive-class.parsed.json similarity index 100% rename from test/integration/fixtures/blocks/core__image__deprecated-1.parsed.json rename to test/integration/fixtures/blocks/core__image__deprecated-v1-add-responsive-class.parsed.json diff --git a/test/integration/fixtures/blocks/core__image__deprecated-1.serialized.html b/test/integration/fixtures/blocks/core__image__deprecated-v1-add-responsive-class.serialized.html similarity index 100% rename from test/integration/fixtures/blocks/core__image__deprecated-1.serialized.html rename to test/integration/fixtures/blocks/core__image__deprecated-v1-add-responsive-class.serialized.html diff --git a/test/integration/fixtures/blocks/core__image__deprecated-2.html b/test/integration/fixtures/blocks/core__image__deprecated-v2-add-is-resized-class.html similarity index 100% rename from test/integration/fixtures/blocks/core__image__deprecated-2.html rename to test/integration/fixtures/blocks/core__image__deprecated-v2-add-is-resized-class.html diff --git a/test/integration/fixtures/blocks/core__image__deprecated-3.json b/test/integration/fixtures/blocks/core__image__deprecated-v2-add-is-resized-class.json similarity index 94% rename from test/integration/fixtures/blocks/core__image__deprecated-3.json rename to test/integration/fixtures/blocks/core__image__deprecated-v2-add-is-resized-class.json index bae213510011ac..8d1d58f3e6fd3e 100644 --- a/test/integration/fixtures/blocks/core__image__deprecated-3.json +++ b/test/integration/fixtures/blocks/core__image__deprecated-v2-add-is-resized-class.json @@ -6,7 +6,7 @@ "align": "left", "url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==", "alt": "", - "caption": "", + "caption": [], "width": 100, "height": 100 }, diff --git a/test/integration/fixtures/blocks/core__image__deprecated-2.parsed.json b/test/integration/fixtures/blocks/core__image__deprecated-v2-add-is-resized-class.parsed.json similarity index 100% rename from test/integration/fixtures/blocks/core__image__deprecated-2.parsed.json rename to test/integration/fixtures/blocks/core__image__deprecated-v2-add-is-resized-class.parsed.json diff --git a/test/integration/fixtures/blocks/core__image__deprecated-2.serialized.html b/test/integration/fixtures/blocks/core__image__deprecated-v2-add-is-resized-class.serialized.html similarity index 100% rename from test/integration/fixtures/blocks/core__image__deprecated-2.serialized.html rename to test/integration/fixtures/blocks/core__image__deprecated-v2-add-is-resized-class.serialized.html diff --git a/test/integration/fixtures/blocks/core__image__deprecated-3.html b/test/integration/fixtures/blocks/core__image__deprecated-v3-add-align-wrapper.html similarity index 100% rename from test/integration/fixtures/blocks/core__image__deprecated-3.html rename to test/integration/fixtures/blocks/core__image__deprecated-v3-add-align-wrapper.html diff --git a/test/integration/fixtures/blocks/core__image__deprecated-2.json b/test/integration/fixtures/blocks/core__image__deprecated-v3-add-align-wrapper.json similarity index 100% rename from test/integration/fixtures/blocks/core__image__deprecated-2.json rename to test/integration/fixtures/blocks/core__image__deprecated-v3-add-align-wrapper.json diff --git a/test/integration/fixtures/blocks/core__image__deprecated-3.parsed.json b/test/integration/fixtures/blocks/core__image__deprecated-v3-add-align-wrapper.parsed.json similarity index 100% rename from test/integration/fixtures/blocks/core__image__deprecated-3.parsed.json rename to test/integration/fixtures/blocks/core__image__deprecated-v3-add-align-wrapper.parsed.json diff --git a/test/integration/fixtures/blocks/core__image__deprecated-3.serialized.html b/test/integration/fixtures/blocks/core__image__deprecated-v3-add-align-wrapper.serialized.html similarity index 100% rename from test/integration/fixtures/blocks/core__image__deprecated-3.serialized.html rename to test/integration/fixtures/blocks/core__image__deprecated-v3-add-align-wrapper.serialized.html diff --git a/test/integration/fixtures/blocks/core__image__deprecated-align-wrapper.html b/test/integration/fixtures/blocks/core__image__deprecated-v4-remove-align-wrapper.html similarity index 100% rename from test/integration/fixtures/blocks/core__image__deprecated-align-wrapper.html rename to test/integration/fixtures/blocks/core__image__deprecated-v4-remove-align-wrapper.html diff --git a/test/integration/fixtures/blocks/core__image__deprecated-align-wrapper.json b/test/integration/fixtures/blocks/core__image__deprecated-v4-remove-align-wrapper.json similarity index 100% rename from test/integration/fixtures/blocks/core__image__deprecated-align-wrapper.json rename to test/integration/fixtures/blocks/core__image__deprecated-v4-remove-align-wrapper.json diff --git a/test/integration/fixtures/blocks/core__image__deprecated-align-wrapper.parsed.json b/test/integration/fixtures/blocks/core__image__deprecated-v4-remove-align-wrapper.parsed.json similarity index 100% rename from test/integration/fixtures/blocks/core__image__deprecated-align-wrapper.parsed.json rename to test/integration/fixtures/blocks/core__image__deprecated-v4-remove-align-wrapper.parsed.json diff --git a/test/integration/fixtures/blocks/core__image__deprecated-align-wrapper.serialized.html b/test/integration/fixtures/blocks/core__image__deprecated-v4-remove-align-wrapper.serialized.html similarity index 100% rename from test/integration/fixtures/blocks/core__image__deprecated-align-wrapper.serialized.html rename to test/integration/fixtures/blocks/core__image__deprecated-v4-remove-align-wrapper.serialized.html diff --git a/test/integration/fixtures/blocks/core__image__deprecated-border-radius-5.html b/test/integration/fixtures/blocks/core__image__deprecated-v5-move-border-radius-style.html similarity index 100% rename from test/integration/fixtures/blocks/core__image__deprecated-border-radius-5.html rename to test/integration/fixtures/blocks/core__image__deprecated-v5-move-border-radius-style.html diff --git a/test/integration/fixtures/blocks/core__image__deprecated-border-radius-5.json b/test/integration/fixtures/blocks/core__image__deprecated-v5-move-border-radius-style.json similarity index 100% rename from test/integration/fixtures/blocks/core__image__deprecated-border-radius-5.json rename to test/integration/fixtures/blocks/core__image__deprecated-v5-move-border-radius-style.json diff --git a/test/integration/fixtures/blocks/core__image__deprecated-border-radius-5.parsed.json b/test/integration/fixtures/blocks/core__image__deprecated-v5-move-border-radius-style.parsed.json similarity index 100% rename from test/integration/fixtures/blocks/core__image__deprecated-border-radius-5.parsed.json rename to test/integration/fixtures/blocks/core__image__deprecated-v5-move-border-radius-style.parsed.json diff --git a/test/integration/fixtures/blocks/core__image__deprecated-border-radius-5.serialized.html b/test/integration/fixtures/blocks/core__image__deprecated-v5-move-border-radius-style.serialized.html similarity index 100% rename from test/integration/fixtures/blocks/core__image__deprecated-border-radius-5.serialized.html rename to test/integration/fixtures/blocks/core__image__deprecated-v5-move-border-radius-style.serialized.html From 69ad3180b5d4a4f42afe8817faed7bb885cc92c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9?= <583546+oandregal@users.noreply.github.com> Date: Thu, 29 Jun 2023 13:59:33 +0200 Subject: [PATCH 081/266] Performance tests: configure as a production environment (#52016) --- bin/plugin/commands/performance.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bin/plugin/commands/performance.js b/bin/plugin/commands/performance.js index 95e74c851c1d6b..1acf6eb6a64f9f 100644 --- a/bin/plugin/commands/performance.js +++ b/bin/plugin/commands/performance.js @@ -330,6 +330,10 @@ async function runPerformanceTests( branches, options ) { path.join( environmentDirectory, '.wp-env.json' ), JSON.stringify( { + config: { + WP_DEBUG: false, + SCRIPT_DEBUG: false, + }, core: 'WordPress/WordPress', plugins: [ path.join( environmentDirectory, 'plugin' ) ], themes: [ From 3fd16c019bee2e0bfa2769c1d68907aba0d482c1 Mon Sep 17 00:00:00 2001 From: Carlos Bravo <37012961+c4rl0sbr4v0@users.noreply.github.com> Date: Thu, 29 Jun 2023 15:15:05 +0200 Subject: [PATCH 082/266] Image block and behaviors: Fix some warnings (#52109) * Fix first warning * Fix second warning - dividing a NaN --- lib/experimental/interactivity-api/blocks.php | 2 ++ packages/block-library/src/image/image.js | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/experimental/interactivity-api/blocks.php b/lib/experimental/interactivity-api/blocks.php index 76955357e6c931..6997f2695c9d68 100644 --- a/lib/experimental/interactivity-api/blocks.php +++ b/lib/experimental/interactivity-api/blocks.php @@ -15,7 +15,9 @@ */ function gutenberg_block_update_interactive_view_script( $metadata ) { if ( + array_key_exists( 'name', $metadata ) && in_array( $metadata['name'], array( 'core/image' ), true ) && + array_key_exists( 'file', $metadata ) && str_contains( $metadata['file'], 'build/block-library/blocks' ) ) { $metadata['viewScript'] = array( 'file:./view-interactivity.min.js' ); diff --git a/packages/block-library/src/image/image.js b/packages/block-library/src/image/image.js index 2dc39751de7101..fd410fe3cbe5f2 100644 --- a/packages/block-library/src/image/image.js +++ b/packages/block-library/src/image/image.js @@ -607,7 +607,8 @@ export default function Image( { const ratio = ( aspectRatio && evalAspectRatio( aspectRatio ) ) || ( width && height && width / height ) || - naturalWidth / naturalHeight; + naturalWidth / naturalHeight || + 1; const currentWidth = ! width && height ? height * ratio : width; const currentHeight = ! height && width ? width / ratio : height; From 774f0a62ab5824600f1613f93752170c60caf1a0 Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 29 Jun 2023 14:40:12 +0100 Subject: [PATCH 083/266] Fix the "exsisting" -> "existing" typo (#52110) --- .../interactivity-api/class-wp-interactivity-store.php | 2 +- lib/experimental/interactivity-api/store.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/experimental/interactivity-api/class-wp-interactivity-store.php b/lib/experimental/interactivity-api/class-wp-interactivity-store.php index 46ca574e667c4c..fe96af4786d3bf 100644 --- a/lib/experimental/interactivity-api/class-wp-interactivity-store.php +++ b/lib/experimental/interactivity-api/class-wp-interactivity-store.php @@ -37,7 +37,7 @@ static function get_data() { /** * Merge data. * - * @param array $data The data that will be merged with the exsisting store. + * @param array $data The data that will be merged with the existing store. */ static function merge_data( $data ) { self::$store = array_replace_recursive( self::$store, $data ); diff --git a/lib/experimental/interactivity-api/store.php b/lib/experimental/interactivity-api/store.php index 88c4b2ebd1038a..5e793514e54c90 100644 --- a/lib/experimental/interactivity-api/store.php +++ b/lib/experimental/interactivity-api/store.php @@ -7,9 +7,9 @@ */ /** - * Merge data with the exsisting store. + * Merge data with the existing store. * - * @param array $data Data that will be merged with the exsisting store. + * @param array $data Data that will be merged with the existing store. * * @return $data The current store data. */ From aab48783ec7e32910eeebfee354cc355771dcab9 Mon Sep 17 00:00:00 2001 From: JuanMa Date: Thu, 29 Jun 2023 14:41:04 +0100 Subject: [PATCH 084/266] first version of the Interactivity API README (#52104) * first version of the Interactivity API README * README v2 upon Luis feedback * headings adjusted * minor adjustments --- packages/interactivity/README.md | 58 +++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/packages/interactivity/README.md b/packages/interactivity/README.md index 508cc06588f752..1643d519555d2a 100644 --- a/packages/interactivity/README.md +++ b/packages/interactivity/README.md @@ -1 +1,57 @@ -# Interactivity +# Interactivity API + +> **Warning** +> **This package is only available in Gutenberg** at the moment and not in WordPress Core as it is still very experimental, and very likely to change. + + +> **Note** +> This package enables the API shared at [Proposal: The Interactivity API – A better developer experience in building interactive blocks](https://make.wordpress.org/core/2023/03/30/proposal-the-interactivity-api-a-better-developer-experience-in-building-interactive-blocks/). As part of an [Open Source project](https://developer.wordpress.org/block-editor/explanations/faq/#the-gutenberg-project) we encourage participation in helping shape this API and the [discussions in GitHub](https://github.com/WordPress/gutenberg/discussions/categories/interactivity-api) is the best place to engage. + +This package can be tested, but it's still very experimental. +The Interactivity API is [being used in some core blocks](https://github.com/search?q=repo%3AWordPress%2Fgutenberg%20%40wordpress%2Finteractivity&type=code) but its use is still very limited. + + +## Frequently Asked Questions + +At this point, some of the questions you have about the Interactivity API may be: + +### What is this? + +This is the base of a new standard to create interactive blocks. Read [the proposal](https://make.wordpress.org/core/2023/03/30/proposal-the-interactivity-api-a-better-developer-experience-in-building-interactive-blocks/) to learn more about this. + +### Can I use it? + +You can test it, but it's still very experimental. + +### How do I get started? + +A "Getting started guide" section of the Interactivity API Docs is in progress. In the meantime check the **Docs & Examples** section below for resources to learn. You can also ask for guidance at the [“Interactivity API” category](https://github.com/WordPress/gutenberg/discussions/categories/interactivity-api) in Gutenberg repo discussions. + +### Where can I ask questions? + +The [“Interactivity API” category](https://github.com/WordPress/gutenberg/discussions/categories/interactivity-api) in Gutenberg repo discussions is the best place to ask questions about the Interactivity API. + +### Where can I share my feedback about the API? + +The [“Interactivity API” category](https://github.com/WordPress/gutenberg/discussions/categories/interactivity-api) in Gutenberg repo discussions is also the best place to share your feedback about the Interactivity API. + +## Installation + +Install the module: + +```bash +npm install @wordpress/interactivity --save +``` + +_This package assumes that your code will run in an **ES2015+** environment. If you're using an environment that has limited or no support for such language features and APIs, you should include [the polyfill shipped in `@wordpress/babel-preset-default`](https://github.com/WordPress/gutenberg/tree/HEAD/packages/babel-preset-default#polyfill) in your code._ + +## Docs & Examples + +Interactivity API is a recent proposal and its Documentation is still in progress. In the meantime, here you have these resources to learn/read more about the Interactivity API: + +- [Proposal: The Interactivity API – A better developer experience in building interactive blocks](https://make.wordpress.org/core/2023/03/30/proposal-the-interactivity-api-a-better-developer-experience-in-building-interactive-blocks/) +- [“Interactivity API” category](https://github.com/WordPress/gutenberg/discussions/categories/interactivity-api) in Gutenberg repo discussions +- Developer Hours sessions ([Americas](https://www.youtube.com/watch?v=RXNoyP2ZiS8&t=664s) & [APAC/EMEA](https://www.youtube.com/watch?v=6ghbrhyAcvA)) +- [wpmovies.dev](http://wpmovies.dev/) demo and its [wp-movies-demo](https://github.com/WordPress/wp-movies-demo) repo + +

Code is Poetry.

From 96232781af5cd0cad1ae91de5c8d83ebae260969 Mon Sep 17 00:00:00 2001 From: Joen A <1204802+jasmussen@users.noreply.github.com> Date: Thu, 29 Jun 2023 15:50:12 +0200 Subject: [PATCH 085/266] Drop-indicator: remove white border. (#52122) --- packages/block-editor/src/components/list-view/style.scss | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/block-editor/src/components/list-view/style.scss b/packages/block-editor/src/components/list-view/style.scss index 0924e98b3f5d45..ab041af51aa1e9 100644 --- a/packages/block-editor/src/components/list-view/style.scss +++ b/packages/block-editor/src/components/list-view/style.scss @@ -408,8 +408,7 @@ $block-navigation-max-indent: 8; .block-editor-list-view-drop-indicator__line { background: var(--wp-admin-theme-color); - height: 6px; - border: 1px solid $white; + height: 4px; border-radius: 4px; } } From c355d5472e2d9c4c5d611f02b5b74b5efb6957a4 Mon Sep 17 00:00:00 2001 From: Dave Smith Date: Thu, 29 Jun 2023 15:23:20 +0100 Subject: [PATCH 086/266] Make Navigation fallback selector private (#51413) * Move selector to become private * adds basic lock functionality * remove useless lock-unlock file * map private selectors to resolvers * Unlock the other usage * only create one fallback per session * Fix core-data duplicate private opt-in * Data: bind resolvers to selectors individually, support private selectors --------- Co-authored-by: Andrei Draganescu Co-authored-by: scruffian Co-authored-by: Jarda Snajdr --- docs/reference-guides/data/data-core.md | 12 --- .../src/navigation/edit/index.js | 3 +- packages/core-data/README.md | 12 --- packages/core-data/src/index.js | 7 +- packages/core-data/src/private-selectors.ts | 13 +++ packages/core-data/src/selectors.ts | 12 --- packages/data/src/redux-store/index.js | 84 ++++++++++--------- .../index.js | 16 +++- 8 files changed, 75 insertions(+), 84 deletions(-) diff --git a/docs/reference-guides/data/data-core.md b/docs/reference-guides/data/data-core.md index 8db3a26dd09772..a66c0991e3d274 100644 --- a/docs/reference-guides/data/data-core.md +++ b/docs/reference-guides/data/data-core.md @@ -329,18 +329,6 @@ _Returns_ - `any`: The entity record's save error. -### getNavigationFallbackId - -Retrieve the fallback Navigation. - -_Parameters_ - -- _state_ `State`: Data state. - -_Returns_ - -- `EntityRecordKey | undefined`: The ID for the fallback Navigation post. - ### getRawEntityRecord Returns the entity's record object by key, with its attributes mapped to their raw values. diff --git a/packages/block-library/src/navigation/edit/index.js b/packages/block-library/src/navigation/edit/index.js index 2fe86825bb6b11..7ec990d5b7389c 100644 --- a/packages/block-library/src/navigation/edit/index.js +++ b/packages/block-library/src/navigation/edit/index.js @@ -69,7 +69,6 @@ import ManageMenusButton from './manage-menus-button'; import MenuInspectorControls from './menu-inspector-controls'; import DeletedNavigationWarning from './deleted-navigation-warning'; import { unlock } from '../../lock-unlock'; - const { useBlockEditingMode } = unlock( blockEditorPrivateApis ); function Navigation( { @@ -224,7 +223,7 @@ function Navigation( { // that automatically saves the menu as an entity when changes are made to the inner blocks. const hasUnsavedBlocks = hasUncontrolledInnerBlocks && ! isEntityAvailable; - const { getNavigationFallbackId } = useSelect( coreStore ); + const { getNavigationFallbackId } = unlock( useSelect( coreStore ) ); const navigationFallbackId = ! ( ref || hasUnsavedBlocks ) ? getNavigationFallbackId() diff --git a/packages/core-data/README.md b/packages/core-data/README.md index ade2293efe167a..63e6e28db08d53 100644 --- a/packages/core-data/README.md +++ b/packages/core-data/README.md @@ -506,18 +506,6 @@ _Returns_ - `any`: The entity record's save error. -### getNavigationFallbackId - -Retrieve the fallback Navigation. - -_Parameters_ - -- _state_ `State`: Data state. - -_Returns_ - -- `EntityRecordKey | undefined`: The ID for the fallback Navigation post. - ### getRawEntityRecord Returns the entity's record object by key, with its attributes mapped to their raw values. diff --git a/packages/core-data/src/index.js b/packages/core-data/src/index.js index c2b491fa8c1ea1..98509d9f0383ba 100644 --- a/packages/core-data/src/index.js +++ b/packages/core-data/src/index.js @@ -13,6 +13,8 @@ import * as resolvers from './resolvers'; import createLocksActions from './locks/actions'; import { rootEntitiesConfig, getMethodName } from './entities'; import { STORE_NAME } from './name'; +import { unlock } from './private-apis'; +import { getNavigationFallbackId } from './private-selectors'; // The entity selectors/resolvers and actions are shortcuts to their generic equivalents // (getEntityRecord, getEntityRecords, updateEntityRecord, updateEntityRecords) @@ -62,7 +64,10 @@ const storeConfig = () => ( { * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/data/README.md#createReduxStore */ export const store = createReduxStore( STORE_NAME, storeConfig() ); -register( store ); +unlock( store ).registerPrivateSelectors( { + getNavigationFallbackId, +} ); +register( store ); // Register store after unlocking private selectors to allow resolvers to use them. export { default as EntityProvider } from './entity-provider'; export * from './entity-provider'; diff --git a/packages/core-data/src/private-selectors.ts b/packages/core-data/src/private-selectors.ts index 0ac2c750239692..1e253b900e1cbb 100644 --- a/packages/core-data/src/private-selectors.ts +++ b/packages/core-data/src/private-selectors.ts @@ -4,6 +4,7 @@ import type { State, UndoEdit } from './selectors'; type Optional< T > = T | undefined; +type EntityRecordKey = string | number; /** * Returns the previous edit from the current undo offset @@ -28,3 +29,15 @@ export function getUndoEdits( state: State ): Optional< UndoEdit[] > { export function getRedoEdits( state: State ): Optional< UndoEdit[] > { return state.undo.list[ state.undo.list.length + state.undo.offset ]; } + +/** + * Retrieve the fallback Navigation. + * + * @param state Data state. + * @return The ID for the fallback Navigation post. + */ +export function getNavigationFallbackId( + state: State +): EntityRecordKey | undefined { + return state.navigationFallbackId; +} diff --git a/packages/core-data/src/selectors.ts b/packages/core-data/src/selectors.ts index a6b7774d37094c..142d24a9d2b8dc 100644 --- a/packages/core-data/src/selectors.ts +++ b/packages/core-data/src/selectors.ts @@ -1257,18 +1257,6 @@ export function getBlockPatternCategories( state: State ): Array< any > { return state.blockPatternCategories; } -/** - * Retrieve the fallback Navigation. - * - * @param state Data state. - * @return The ID for the fallback Navigation post. - */ -export function getNavigationFallbackId( - state: State -): EntityRecordKey | undefined { - return state.navigationFallbackId; -} - /** * Returns the revisions of the current global styles theme. * diff --git a/packages/data/src/redux-store/index.js b/packages/data/src/redux-store/index.js index 583a1ebed1dbba..43357b766f618e 100644 --- a/packages/data/src/redux-store/index.js +++ b/packages/data/src/redux-store/index.js @@ -106,10 +106,10 @@ function createBindingCache( bind ) { const cache = new WeakMap(); return { - get( item ) { + get( item, itemName ) { let boundItem = cache.get( item ); if ( ! boundItem ) { - boundItem = bind( item ); + boundItem = bind( item, itemName ); cache.set( item, boundItem ); } return boundItem; @@ -212,7 +212,7 @@ export default function createReduxStore( key, options ) { get: ( target, prop ) => { const privateAction = privateActions[ prop ]; return privateAction - ? boundPrivateActions.get( privateAction ) + ? boundPrivateActions.get( privateAction, prop ) : actions[ prop ]; }, } ); @@ -224,7 +224,11 @@ export default function createReduxStore( key, options ) { lock( actions, allActions ); - function bindSelector( selector ) { + const resolvers = options.resolvers + ? mapResolvers( options.resolvers ) + : {}; + + function bindSelector( selector, selectorName ) { if ( selector.isRegistrySelector ) { selector.registry = registry; } @@ -232,8 +236,20 @@ export default function createReduxStore( key, options ) { const state = store.__unstableOriginalGetState(); return selector( state.root, ...args ); }; - boundSelector.hasResolver = false; - return boundSelector; + + const resolver = resolvers[ selectorName ]; + if ( ! resolver ) { + boundSelector.hasResolver = false; + return boundSelector; + } + + return mapSelectorWithResolver( + boundSelector, + selectorName, + resolver, + store, + resolversCache + ); } function bindMetadataSelector( selector ) { @@ -245,35 +261,26 @@ export default function createReduxStore( key, options ) { return boundSelector; } - let selectors = { + const selectors = { ...mapValues( metadataSelectors, bindMetadataSelector ), ...mapValues( options.selectors, bindSelector ), }; - let resolvers; - if ( options.resolvers ) { - resolvers = mapResolvers( options.resolvers ); - selectors = mapSelectorsWithResolvers( - selectors, - resolvers, - store, - resolversCache - ); - } - const boundPrivateSelectors = createBindingCache( bindSelector ); // Pre-bind the private selectors that have been registered by the time of // instantiation, so that registry selectors are bound to the registry. - for ( const privateSelector of Object.values( privateSelectors ) ) { - boundPrivateSelectors.get( privateSelector ); + for ( const [ selectorName, selector ] of Object.entries( + privateSelectors + ) ) { + boundPrivateSelectors.get( selector, selectorName ); } const allSelectors = new Proxy( () => {}, { get: ( target, prop ) => { const privateSelector = privateSelectors[ prop ]; return privateSelector - ? boundPrivateSelectors.get( privateSelector ) + ? boundPrivateSelectors.get( privateSelector, prop ) : selectors[ prop ]; }, } ); @@ -530,22 +537,24 @@ function mapResolvers( resolvers ) { } /** - * Returns resolvers with matched selectors for a given namespace. + * Returns a selector with a matched resolver. * Resolvers are side effects invoked once per argument set of a given selector call, * used in ensuring that the data needs for the selector are satisfied. * - * @param {Object} selectors The current selectors to be modified. - * @param {Object} resolvers Resolvers to register. + * @param {Object} selector The selector function to be bound. + * @param {string} selectorName The selector name. + * @param {Object} resolver Resolver to call. * @param {Object} store The redux store to which the resolvers should be mapped. * @param {Object} resolversCache Resolvers Cache. */ -function mapSelectorsWithResolvers( - selectors, - resolvers, +function mapSelectorWithResolver( + selector, + selectorName, + resolver, store, resolversCache ) { - function fulfillSelector( resolver, selectorName, args ) { + function fulfillSelector( args ) { const state = store.getState(); if ( @@ -591,17 +600,10 @@ function mapSelectorsWithResolvers( }, 0 ); } - return mapValues( selectors, ( selector, selectorName ) => { - const resolver = resolvers[ selectorName ]; - if ( ! resolver ) { - return selector; - } - - const selectorResolver = ( ...args ) => { - fulfillSelector( resolver, selectorName, args ); - return selector( ...args ); - }; - selectorResolver.hasResolver = true; - return selectorResolver; - } ); + const selectorResolver = ( ...args ) => { + fulfillSelector( args ); + return selector( ...args ); + }; + selectorResolver.hasResolver = true; + return selectorResolver; } 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 57ebfd5a2e9533..9d36e956cdca47 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 @@ -21,6 +21,7 @@ import { PRELOADED_NAVIGATION_MENUS_QUERY } from './constants'; import { useLink } from '../routes/link'; import SingleNavigationMenu from '../sidebar-navigation-screen-navigation-menu/single-navigation-menu'; import useNavigationMenuHandlers from '../sidebar-navigation-screen-navigation-menu/use-navigation-menu-handlers'; +import { unlock } from '../../lock-unlock'; // Copied from packages/block-library/src/navigation/edit/navigation-menu-selector.js. function buildMenuLabel( title, id, status ) { @@ -41,6 +42,9 @@ 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() { const { records: navigationMenus, @@ -55,18 +59,22 @@ export default function SidebarNavigationScreenNavigationMenus() { const isLoading = isResolvingNavigationMenus && ! hasResolvedNavigationMenus; - const getNavigationFallbackId = useSelect( - ( select ) => select( coreStore ).getNavigationFallbackId - ); + const { getNavigationFallbackId } = unlock( useSelect( coreStore ) ); const firstNavigationMenu = navigationMenus?.[ 0 ]; + // Save a boolean to prevent us creating a fallback more than once per session. + if ( firstNavigationMenu ) { + hasCreatedFallback = true; + } + // If there is no navigation menu found // then trigger fallback algorithm to create one. if ( ! firstNavigationMenu && ! isResolvingNavigationMenus && - hasResolvedNavigationMenus + hasResolvedNavigationMenus && + ! hasCreatedFallback ) { getNavigationFallbackId(); } From 585ddcd70ed5ed915f5943dacbeae4efe75f19b8 Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Thu, 29 Jun 2023 15:24:25 -0400 Subject: [PATCH 087/266] feat: Rename Reusable blocks to Patterns (#51704) * feat: Rename Reusable blocks to Synced patterns Align with new web editor terminology: 487f230 * feat: Rename "Reusable blocks" to "Synced patterns" in support note Align with new web editor terminology: 487f230 * docs: Add change log entry --- .../src/components/inserter/reusable-blocks-tab.native.js | 4 ++-- .../block-editor/src/components/inserter/tabs.native.js | 6 +++++- packages/block-library/src/block/edit.native.js | 4 ++-- packages/block-library/src/block/test/edit.native.js | 8 ++++---- packages/react-native-editor/CHANGELOG.md | 1 + 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/packages/block-editor/src/components/inserter/reusable-blocks-tab.native.js b/packages/block-editor/src/components/inserter/reusable-blocks-tab.native.js index d50a5a99f2728a..1da84507f17a28 100644 --- a/packages/block-editor/src/components/inserter/reusable-blocks-tab.native.js +++ b/packages/block-editor/src/components/inserter/reusable-blocks-tab.native.js @@ -28,11 +28,11 @@ function ReusableBlocksTab( { onSelect, rootClientId, listProps } ) { return ( ); } diff --git a/packages/block-editor/src/components/inserter/tabs.native.js b/packages/block-editor/src/components/inserter/tabs.native.js index 452305663358af..a921f8e4c5794c 100644 --- a/packages/block-editor/src/components/inserter/tabs.native.js +++ b/packages/block-editor/src/components/inserter/tabs.native.js @@ -142,7 +142,11 @@ InserterTabs.Control = TabsControl; InserterTabs.getTabs = () => [ { name: 'blocks', title: __( 'Blocks' ), component: BlockTypesTab }, - { name: 'reusable', title: __( 'Reusable' ), component: ReusableBlocksTab }, + { + name: 'reusable', + title: __( 'Synced patterns' ), + component: ReusableBlocksTab, + }, ]; export default InserterTabs; diff --git a/packages/block-library/src/block/edit.native.js b/packages/block-library/src/block/edit.native.js index 3466baa6bad713..ddc22c01e40def 100644 --- a/packages/block-library/src/block/edit.native.js +++ b/packages/block-library/src/block/edit.native.js @@ -148,14 +148,14 @@ export default function ReusableBlockEdit( { ? sprintf( /* translators: %s: name of the host app (e.g. WordPress) */ __( - 'Editing reusable blocks is not yet supported on %s for Android' + 'Editing synced patterns is not yet supported on %s for Android' ), hostAppNamespace ) : sprintf( /* translators: %s: name of the host app (e.g. WordPress) */ __( - 'Editing reusable blocks is not yet supported on %s for iOS' + 'Editing synced patterns is not yet supported on %s for iOS' ), hostAppNamespace ); diff --git a/packages/block-library/src/block/test/edit.native.js b/packages/block-library/src/block/test/edit.native.js index 06c26435729a45..1e6c43b5bc4456 100644 --- a/packages/block-library/src/block/test/edit.native.js +++ b/packages/block-library/src/block/test/edit.native.js @@ -56,8 +56,8 @@ afterAll( () => { } ); } ); -describe( 'Reusable block', () => { - it( 'inserts a reusable block', async () => { +describe( 'Synced patterns', () => { + it( 'inserts a synced pattern', async () => { // We have to use different ids because entities are cached in memory. const reusableBlockMock1 = getMockedReusableBlock( 1 ); const reusableBlockMock2 = getMockedReusableBlock( 2 ); @@ -86,7 +86,7 @@ describe( 'Reusable block', () => { fireEvent.press( await screen.findByLabelText( 'Add block' ) ); // Navigate to reusable tab. - const reusableSegment = await screen.findByText( 'Reusable' ); + const reusableSegment = await screen.findByText( 'Synced patterns' ); // onLayout event is required by Segment component. fireEvent( reusableSegment, 'layout', { nativeEvent: { @@ -98,7 +98,7 @@ describe( 'Reusable block', () => { fireEvent.press( reusableSegment ); const reusableBlockList = screen.getByTestId( - 'InserterUI-ReusableBlocks' + 'InserterUI-SyncedPatterns' ); // onScroll event used to force the FlatList to render all items. fireEvent.scroll( reusableBlockList, { diff --git a/packages/react-native-editor/CHANGELOG.md b/packages/react-native-editor/CHANGELOG.md index 3d72851e7f0c95..f235b6e36b51e4 100644 --- a/packages/react-native-editor/CHANGELOG.md +++ b/packages/react-native-editor/CHANGELOG.md @@ -10,6 +10,7 @@ For each user feature we should also add a importance categorization label to i --> ## Unreleased +- [*] Rename "Reusable blocks" to "Synced patterns", aligning with the web editor. [#51704] ## 1.98.0 - [*] Image block - Fix issue where in some cases the image doesn't display the right aspect ratio [#51463] From 096f6f5d7cf91514a07a848566a669514995c058 Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Fri, 30 Jun 2023 05:15:54 +0900 Subject: [PATCH 088/266] [Documentation] Add examples for core/keyboard-shortcut package (#42831) * Add selector examples * Add action examples --------- Co-authored-by: Ryan Welcher --- .../data/data-core-keyboard-shortcuts.md | 320 +++++++++++++++++- .../keyboard-shortcuts/src/store/actions.js | 68 ++++ .../keyboard-shortcuts/src/store/selectors.js | 234 +++++++++++++ 3 files changed, 621 insertions(+), 1 deletion(-) diff --git a/docs/reference-guides/data/data-core-keyboard-shortcuts.md b/docs/reference-guides/data/data-core-keyboard-shortcuts.md index 512668650d04bf..076333720786ed 100644 --- a/docs/reference-guides/data/data-core-keyboard-shortcuts.md +++ b/docs/reference-guides/data/data-core-keyboard-shortcuts.md @@ -8,12 +8,104 @@ Namespace: `core/keyboard-shortcuts`. ### getAllShortcutKeyCombinations -Undocumented declaration. +Returns the shortcuts that include aliases for a given shortcut name. + +_Usage_ + +```js +import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; +import { useSelect } from '@wordpress/data'; +import { createInterpolateElement } from '@wordpress/element'; +import { sprintf } from '@wordpress/i18n'; + +const ExampleComponent = () => { + const allShortcutKeyCombinations = useSelect( + ( select ) => + select( keyboardShortcutsStore ).getAllShortcutKeyCombinations( + 'core/edit-post/next-region' + ), + [] + ); + + return ( + allShortcutKeyCombinations.length > 0 && ( +
    + { allShortcutKeyCombinations.map( + ( { character, modifier }, index ) => ( +
  • + { createInterpolateElement( + sprintf( + 'Character: %s / Modifier: %s', + character, + modifier + ), + { + code: , + } + ) } +
  • + ) + ) } +
+ ) + ); +}; +``` + +_Parameters_ + +- _state_ `Object`: Global state. +- _name_ `string`: Shortcut name. + +_Returns_ + +- `WPShortcutKeyCombination[]`: Key combinations. ### getAllShortcutRawKeyCombinations Returns the raw representation of all the keyboard combinations of a given shortcut name. +_Usage_ + +```js +import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; +import { useSelect } from '@wordpress/data'; +import { createInterpolateElement } from '@wordpress/element'; +import { sprintf } from '@wordpress/i18n'; + +const ExampleComponent = () => { + const allShortcutRawKeyCombinations = useSelect( + ( select ) => + select( keyboardShortcutsStore ).getAllShortcutRawKeyCombinations( + 'core/edit-post/next-region' + ), + [] + ); + + return ( + allShortcutRawKeyCombinations.length > 0 && ( +
    + { allShortcutRawKeyCombinations.map( + ( shortcutRawKeyCombination, index ) => ( +
  • + { createInterpolateElement( + sprintf( + ' %s', + shortcutRawKeyCombination + ), + { + code: , + } + ) } +
  • + ) + ) } +
+ ) + ); +}; +``` + _Parameters_ - _state_ `Object`: Global state. @@ -27,6 +119,31 @@ _Returns_ Returns the shortcut names list for a given category name. +_Usage_ + +```js +import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; +import { useSelect } from '@wordpress/data'; + +const ExampleComponent = () => { + const categoryShortcuts = useSelect( + ( select ) => + select( keyboardShortcutsStore ).getCategoryShortcuts( 'block' ), + [] + ); + + return ( + categoryShortcuts.length > 0 && ( +
    + { categoryShortcuts.map( ( categoryShortcut ) => ( +
  • { categoryShortcut }
  • + ) ) } +
+ ) + ); +}; +``` + _Parameters_ - _state_ `Object`: Global state. @@ -40,6 +157,45 @@ _Returns_ Returns the aliases for a given shortcut name. +_Usage_ + +```js +import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; +import { useSelect } from '@wordpress/data'; +import { createInterpolateElement } from '@wordpress/element'; +import { sprintf } from '@wordpress/i18n'; +const ExampleComponent = () => { + const shortcutAliases = useSelect( + ( select ) => + select( keyboardShortcutsStore ).getShortcutAliases( + 'core/edit-post/next-region' + ), + [] + ); + + return ( + shortcutAliases.length > 0 && ( +
    + { shortcutAliases.map( ( { character, modifier }, index ) => ( +
  • + { createInterpolateElement( + sprintf( + 'Character: %s / Modifier: %s', + character, + modifier + ), + { + code: , + } + ) } +
  • + ) ) } +
+ ) + ); +}; +``` + _Parameters_ - _state_ `Object`: Global state. @@ -53,6 +209,29 @@ _Returns_ Returns the shortcut description given its name. +_Usage_ + +```js +import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; +import { useSelect } from '@wordpress/data'; +import { __ } from '@wordpress/i18n'; +const ExampleComponent = () => { + const shortcutDescription = useSelect( + ( select ) => + select( keyboardShortcutsStore ).getShortcutDescription( + 'core/edit-post/next-region' + ), + [] + ); + + return shortcutDescription ? ( +
{ shortcutDescription }
+ ) : ( +
{ __( 'No description.' ) }
+ ); +}; +``` + _Parameters_ - _state_ `Object`: Global state. @@ -66,6 +245,39 @@ _Returns_ Returns the main key combination for a given shortcut name. +_Usage_ + +```js +import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; +import { useSelect } from '@wordpress/data'; +import { createInterpolateElement } from '@wordpress/element'; +import { sprintf } from '@wordpress/i18n'; +const ExampleComponent = () => { + const { character, modifier } = useSelect( + ( select ) => + select( keyboardShortcutsStore ).getShortcutKeyCombination( + 'core/edit-post/next-region' + ), + [] + ); + + return ( +
+ { createInterpolateElement( + sprintf( + 'Character: %s / Modifier: %s', + character, + modifier + ), + { + code: , + } + ) } +
+ ); +}; +``` + _Parameters_ - _state_ `Object`: Global state. @@ -79,6 +291,42 @@ _Returns_ Returns a string representing the main key combination for a given shortcut name. +_Usage_ + +```js +import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; +import { useSelect } from '@wordpress/data'; +import { sprintf } from '@wordpress/i18n'; + +const ExampleComponent = () => { + const { display, raw, ariaLabel } = useSelect( ( select ) => { + return { + display: select( keyboardShortcutsStore ).getShortcutRepresentation( + 'core/edit-post/next-region' + ), + raw: select( keyboardShortcutsStore ).getShortcutRepresentation( + 'core/edit-post/next-region', + 'raw' + ), + ariaLabel: select( + keyboardShortcutsStore + ).getShortcutRepresentation( + 'core/edit-post/next-region', + 'ariaLabel' + ), + }; + }, [] ); + + return ( +
    +
  • { sprintf( 'display string: %s', display ) }
  • +
  • { sprintf( 'raw string: %s', raw ) }
  • +
  • { sprintf( 'ariaLabel string: %s', ariaLabel ) }
  • +
+ ); +}; +``` + _Parameters_ - _state_ `Object`: Global state. @@ -99,6 +347,45 @@ _Returns_ Returns an action object used to register a new keyboard shortcut. +_Usage_ + +```js +import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; +import { useSelect, useDispatch } from '@wordpress/data'; +import { useEffect } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; + +const ExampleComponent = () => { + const { registerShortcut } = useDispatch( keyboardShortcutsStore ); + + useEffect( () => { + registerShortcut( { + name: 'custom/my-custom-shortcut', + category: 'my-category', + description: __( 'My custom shortcut' ), + keyCombination: { + modifier: 'primary', + character: 'j', + }, + } ); + }, [] ); + + const shortcut = useSelect( + ( select ) => + select( keyboardShortcutsStore ).getShortcutKeyCombination( + 'custom/my-custom-shortcut' + ), + [] + ); + + return shortcut ? ( +

{ __( 'Shortcut is registered.' ) }

+ ) : ( +

{ __( 'Shortcut is not registered.' ) }

+ ); +}; +``` + _Parameters_ - _config_ `WPShortcutConfig`: Shortcut config. @@ -111,6 +398,37 @@ _Returns_ Returns an action object used to unregister a keyboard shortcut. +_Usage_ + +```js +import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; +import { useSelect, useDispatch } from '@wordpress/data'; +import { useEffect } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; + +const ExampleComponent = () => { + const { unregisterShortcut } = useDispatch( keyboardShortcutsStore ); + + useEffect( () => { + unregisterShortcut( 'core/edit-post/next-region' ); + }, [] ); + + const shortcut = useSelect( + ( select ) => + select( keyboardShortcutsStore ).getShortcutKeyCombination( + 'core/edit-post/next-region' + ), + [] + ); + + return shortcut ? ( +

{ __( 'Shortcut is not unregistered.' ) }

+ ) : ( +

{ __( 'Shortcut is unregistered.' ) }

+ ); +}; +``` + _Parameters_ - _name_ `string`: Shortcut name. diff --git a/packages/keyboard-shortcuts/src/store/actions.js b/packages/keyboard-shortcuts/src/store/actions.js index b4cb625287d8fb..d45a23a09f21da 100644 --- a/packages/keyboard-shortcuts/src/store/actions.js +++ b/packages/keyboard-shortcuts/src/store/actions.js @@ -26,6 +26,44 @@ * * @param {WPShortcutConfig} config Shortcut config. * + * @example + * + *```js + * import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; + * import { useSelect, useDispatch } from '@wordpress/data'; + * import { useEffect } from '@wordpress/element'; + * import { __ } from '@wordpress/i18n'; + * + * const ExampleComponent = () => { + * const { registerShortcut } = useDispatch( keyboardShortcutsStore ); + * + * useEffect( () => { + * registerShortcut( { + * name: 'custom/my-custom-shortcut', + * category: 'my-category', + * description: __( 'My custom shortcut' ), + * keyCombination: { + * modifier: 'primary', + * character: 'j', + * }, + * } ); + * }, [] ); + * + * const shortcut = useSelect( + * ( select ) => + * select( keyboardShortcutsStore ).getShortcutKeyCombination( + * 'custom/my-custom-shortcut' + * ), + * [] + * ); + * + * return shortcut ? ( + *

{ __( 'Shortcut is registered.' ) }

+ * ) : ( + *

{ __( 'Shortcut is not registered.' ) }

+ * ); + * }; + *``` * @return {Object} action. */ export function registerShortcut( { @@ -50,6 +88,36 @@ export function registerShortcut( { * * @param {string} name Shortcut name. * + * @example + * + *```js + * import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; + * import { useSelect, useDispatch } from '@wordpress/data'; + * import { useEffect } from '@wordpress/element'; + * import { __ } from '@wordpress/i18n'; + * + * const ExampleComponent = () => { + * const { unregisterShortcut } = useDispatch( keyboardShortcutsStore ); + * + * useEffect( () => { + * unregisterShortcut( 'core/edit-post/next-region' ); + * }, [] ); + * + * const shortcut = useSelect( + * ( select ) => + * select( keyboardShortcutsStore ).getShortcutKeyCombination( + * 'core/edit-post/next-region' + * ), + * [] + * ); + * + * return shortcut ? ( + *

{ __( 'Shortcut is not unregistered.' ) }

+ * ) : ( + *

{ __( 'Shortcut is unregistered.' ) }

+ * ); + * }; + *``` * @return {Object} action. */ export function unregisterShortcut( name ) { diff --git a/packages/keyboard-shortcuts/src/store/selectors.js b/packages/keyboard-shortcuts/src/store/selectors.js index 45acf70f77d200..2d4f5976247b05 100644 --- a/packages/keyboard-shortcuts/src/store/selectors.js +++ b/packages/keyboard-shortcuts/src/store/selectors.js @@ -64,6 +64,39 @@ function getKeyCombinationRepresentation( shortcut, representation ) { * @param {Object} state Global state. * @param {string} name Shortcut name. * + * @example + * + *```js + * import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; + * import { useSelect } from '@wordpress/data'; + * import { createInterpolateElement } from '@wordpress/element'; + * import { sprintf } from '@wordpress/i18n'; + * const ExampleComponent = () => { + * const {character, modifier} = useSelect( + * ( select ) => + * select( keyboardShortcutsStore ).getShortcutKeyCombination( + * 'core/edit-post/next-region' + * ), + * [] + * ); + * + * return ( + *
+ * { createInterpolateElement( + * sprintf( + * 'Character: %s / Modifier: %s', + * character, + * modifier + * ), + * { + * code: , + * } + * ) } + *
+ * ); + * }; + *``` + * * @return {WPShortcutKeyCombination?} Key combination. */ export function getShortcutKeyCombination( state, name ) { @@ -77,6 +110,34 @@ export function getShortcutKeyCombination( state, name ) { * @param {string} name Shortcut name. * @param {keyof FORMATTING_METHODS} representation Type of representation * (display, raw, ariaLabel). + * @example + * + *```js + * import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; + * import { useSelect } from '@wordpress/data'; + * import { sprintf } from '@wordpress/i18n'; + * + * const ExampleComponent = () => { + * const {display, raw, ariaLabel} = useSelect( + * ( select ) =>{ + * return { + * display: select( keyboardShortcutsStore ).getShortcutRepresentation('core/edit-post/next-region' ), + * raw: select( keyboardShortcutsStore ).getShortcutRepresentation('core/edit-post/next-region','raw' ), + * ariaLabel: select( keyboardShortcutsStore ).getShortcutRepresentation('core/edit-post/next-region', 'ariaLabel') + * } + * }, + * [] + * ); + * + * return ( + *
    + *
  • { sprintf( 'display string: %s', display ) }
  • + *
  • { sprintf( 'raw string: %s', raw ) }
  • + *
  • { sprintf( 'ariaLabel string: %s', ariaLabel ) }
  • + *
+ * ); + * }; + *``` * * @return {string?} Shortcut representation. */ @@ -95,6 +156,26 @@ export function getShortcutRepresentation( * @param {Object} state Global state. * @param {string} name Shortcut name. * + * @example + * + *```js + * import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; + * import { useSelect } from '@wordpress/data'; + * import { __ } from '@wordpress/i18n'; + * const ExampleComponent = () => { + * const shortcutDescription = useSelect( + * ( select ) => + * select( keyboardShortcutsStore ).getShortcutDescription( 'core/edit-post/next-region' ), + * [] + * ); + * + * return shortcutDescription ? ( + *
{ shortcutDescription }
+ * ) : ( + *
{ __( 'No description.' ) }
+ * ); + * }; + *``` * @return {string?} Shortcut description. */ export function getShortcutDescription( state, name ) { @@ -106,6 +187,44 @@ export function getShortcutDescription( state, name ) { * * @param {Object} state Global state. * @param {string} name Shortcut name. + * @example + * + *```js + * import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; + * import { useSelect } from '@wordpress/data'; + * import { createInterpolateElement } from '@wordpress/element'; + * import { sprintf } from '@wordpress/i18n'; + * const ExampleComponent = () => { + * const shortcutAliases = useSelect( + * ( select ) => + * select( keyboardShortcutsStore ).getShortcutAliases( + * 'core/edit-post/next-region' + * ), + * [] + * ); + * + * return ( + * shortcutAliases.length > 0 && ( + *
    + * { shortcutAliases.map( ( { character, modifier }, index ) => ( + *
  • + * { createInterpolateElement( + * sprintf( + * 'Character: %s / Modifier: %s', + * character, + * modifier + * ), + * { + * code: , + * } + * ) } + *
  • + * ) ) } + *
+ * ) + * ); + * }; + *``` * * @return {WPShortcutKeyCombination[]} Key combinations. */ @@ -115,6 +234,55 @@ export function getShortcutAliases( state, name ) { : EMPTY_ARRAY; } +/** + * Returns the shortcuts that include aliases for a given shortcut name. + * + * @param {Object} state Global state. + * @param {string} name Shortcut name. + * @example + * + *```js + * import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; + * import { useSelect } from '@wordpress/data'; + * import { createInterpolateElement } from '@wordpress/element'; + * import { sprintf } from '@wordpress/i18n'; + * + * const ExampleComponent = () => { + * const allShortcutKeyCombinations = useSelect( + * ( select ) => + * select( keyboardShortcutsStore ).getAllShortcutKeyCombinations( + * 'core/edit-post/next-region' + * ), + * [] + * ); + * + * return ( + * allShortcutKeyCombinations.length > 0 && ( + *
    + * { allShortcutKeyCombinations.map( + * ( { character, modifier }, index ) => ( + *
  • + * { createInterpolateElement( + * sprintf( + * 'Character: %s / Modifier: %s', + * character, + * modifier + * ), + * { + * code: , + * } + * ) } + *
  • + * ) + * ) } + *
+ * ) + * ); + * }; + *``` + * + * @return {WPShortcutKeyCombination[]} Key combinations. + */ export const getAllShortcutKeyCombinations = createSelector( ( state, name ) => { return [ @@ -131,6 +299,47 @@ export const getAllShortcutKeyCombinations = createSelector( * @param {Object} state Global state. * @param {string} name Shortcut name. * + * @example + * + *```js + * import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; + * import { useSelect } from '@wordpress/data'; + * import { createInterpolateElement } from '@wordpress/element'; + * import { sprintf } from '@wordpress/i18n'; + * + * const ExampleComponent = () => { + * const allShortcutRawKeyCombinations = useSelect( + * ( select ) => + * select( keyboardShortcutsStore ).getAllShortcutRawKeyCombinations( + * 'core/edit-post/next-region' + * ), + * [] + * ); + * + * return ( + * allShortcutRawKeyCombinations.length > 0 && ( + *
    + * { allShortcutRawKeyCombinations.map( + * ( shortcutRawKeyCombination, index ) => ( + *
  • + * { createInterpolateElement( + * sprintf( + * ' %s', + * shortcutRawKeyCombination + * ), + * { + * code: , + * } + * ) } + *
  • + * ) + * ) } + *
+ * ) + * ); + * }; + *``` + * * @return {string[]} Shortcuts. */ export const getAllShortcutRawKeyCombinations = createSelector( @@ -148,7 +357,32 @@ export const getAllShortcutRawKeyCombinations = createSelector( * * @param {Object} state Global state. * @param {string} name Category name. + * @example + * + *```js + * import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; + * import { useSelect } from '@wordpress/data'; + * + * const ExampleComponent = () => { + * const categoryShortcuts = useSelect( + * ( select ) => + * select( keyboardShortcutsStore ).getCategoryShortcuts( + * 'block' + * ), + * [] + * ); * + * return ( + * categoryShortcuts.length > 0 && ( + *
    + * { categoryShortcuts.map( ( categoryShortcut ) => ( + *
  • { categoryShortcut }
  • + * ) ) } + *
+ * ) + * ); + * }; + *``` * @return {string[]} Shortcut names. */ export const getCategoryShortcuts = createSelector( From 4e174438426ed6c6245bc04ff15894fcec104d42 Mon Sep 17 00:00:00 2001 From: Ryan Welcher Date: Thu, 29 Jun 2023 23:17:10 +0300 Subject: [PATCH 089/266] Add @examples to the @wordpress/rich-text package selectors and hide the actions from documentation. (#52089) * Add example for getFormatTypes. * Add example for getFormatType. * Add examples for the rest of the selectors. * Ignore the actions in favor of using the appropriate functions. --- .../data/data-core-rich-text.md | 118 ++++++++++++++---- packages/rich-text/src/store/actions.js | 7 ++ packages/rich-text/src/store/selectors.js | 92 ++++++++++++++ 3 files changed, 194 insertions(+), 23 deletions(-) diff --git a/docs/reference-guides/data/data-core-rich-text.md b/docs/reference-guides/data/data-core-rich-text.md index da0e4d61197c0c..55220b3ca9c5d9 100644 --- a/docs/reference-guides/data/data-core-rich-text.md +++ b/docs/reference-guides/data/data-core-rich-text.md @@ -10,6 +10,35 @@ Namespace: `core/rich-text`. Returns a format type by name. +_Usage_ + +```js +import { __, sprintf } from '@wordpress/i18n'; +import { store as richTextStore } from '@wordpress/rich-text'; +import { useSelect } from '@wordpress/data'; + +const ExampleComponent = () => { + const { getFormatType } = useSelect( + ( select ) => select( richTextStore ), + [] + ); + + const boldFormat = getFormatType( 'core/bold' ); + + return boldFormat ? ( +
    + { Object.entries( boldFormat )?.map( ( [ key, value ] ) => ( +
  • + { key } : { value } +
  • + ) ) } +
+ ) : ( + __( 'Not Found' ) + ; +}; +``` + _Parameters_ - _state_ `Object`: Data state. @@ -23,6 +52,25 @@ _Returns_ Gets the format type, if any, that can handle a bare element (without a data-format-type attribute), given the tag name of this element. +_Usage_ + +```js +import { __, sprintf } from '@wordpress/i18n'; +import { store as richTextStore } from '@wordpress/rich-text'; +import { useSelect } from '@wordpress/data'; + +const ExampleComponent = () => { + const { getFormatTypeForBareElement } = useSelect( + ( select ) => select( richTextStore ), + [] + ); + + const format = getFormatTypeForBareElement( 'strong' ); + + return format &&

{ sprintf( __( 'Format name: %s' ), format.name ) }

; +}; +``` + _Parameters_ - _state_ `Object`: Data state. @@ -36,6 +84,25 @@ _Returns_ Gets the format type, if any, that can handle an element, given its classes. +_Usage_ + +```js +import { __, sprintf } from '@wordpress/i18n'; +import { store as richTextStore } from '@wordpress/rich-text'; +import { useSelect } from '@wordpress/data'; + +const ExampleComponent = () => { + const { getFormatTypeForClassName } = useSelect( + ( select ) => select( richTextStore ), + [] + ); + + const format = getFormatTypeForClassName( 'has-inline-color' ); + + return format &&

{ sprintf( __( 'Format name: %s' ), format.name ) }

; +}; +``` + _Parameters_ - _state_ `Object`: Data state. @@ -49,6 +116,33 @@ _Returns_ Returns all the available format types. +_Usage_ + +```js +import { __, sprintf } from '@wordpress/i18n'; +import { store as richTextStore } from '@wordpress/rich-text'; +import { useSelect } from '@wordpress/data'; + +const ExampleComponent = () => { + const { getFormatTypes } = useSelect( + ( select ) => select( richTextStore ), + [] + ); + + const availableFormats = getFormatTypes(); + + return availableFormats ? ( +
    + { availableFormats?.map( ( format ) => ( +
  • { format.name }
  • + ) ) } +
+ ) : ( + __( 'No Formats available' ) + ); +}; +``` + _Parameters_ - _state_ `Object`: Data state. @@ -63,28 +157,6 @@ _Returns_ -### addFormatTypes - -Returns an action object used in signalling that format types have been added. - -_Parameters_ - -- _formatTypes_ `Array|Object`: Format types received. - -_Returns_ - -- `Object`: Action object. - -### removeFormatTypes - -Returns an action object used to remove a registered format type. - -_Parameters_ - -- _names_ `string|Array`: Format name. - -_Returns_ - -- `Object`: Action object. +Nothing to document. diff --git a/packages/rich-text/src/store/actions.js b/packages/rich-text/src/store/actions.js index b8abea4d0aa29b..4fa522f5ec6e3c 100644 --- a/packages/rich-text/src/store/actions.js +++ b/packages/rich-text/src/store/actions.js @@ -1,6 +1,9 @@ /** * Returns an action object used in signalling that format types have been * added. + * Ignored from documentation as registerFormatType should be used instead from @wordpress/rich-text + * + * @ignore * * @param {Array|Object} formatTypes Format types received. * @@ -18,6 +21,10 @@ export function addFormatTypes( formatTypes ) { /** * Returns an action object used to remove a registered format type. * + * Ignored from documentation as unregisterFormatType should be used instead from @wordpress/rich-text + * + * @ignore + * * @param {string|Array} names Format name. * * @return {Object} Action object. diff --git a/packages/rich-text/src/store/selectors.js b/packages/rich-text/src/store/selectors.js index 52aa4dead99bf7..cdc597aee0d990 100644 --- a/packages/rich-text/src/store/selectors.js +++ b/packages/rich-text/src/store/selectors.js @@ -8,6 +8,32 @@ import createSelector from 'rememo'; * * @param {Object} state Data state. * + * @example + * ```js + * import { __, sprintf } from '@wordpress/i18n'; + * import { store as richTextStore } from '@wordpress/rich-text'; + * import { useSelect } from '@wordpress/data'; + * + * const ExampleComponent = () => { + * const { getFormatTypes } = useSelect( + * ( select ) => select( richTextStore ), + * [] + * ); + * + * const availableFormats = getFormatTypes(); + * + * return availableFormats ? ( + *
    + * { availableFormats?.map( ( format ) => ( + *
  • { format.name }
  • + * ) ) } + *
+ * ) : ( + * __( 'No Formats available' ) + * ); + * }; + * ``` + * * @return {Array} Format types. */ export const getFormatTypes = createSelector( @@ -21,6 +47,34 @@ export const getFormatTypes = createSelector( * @param {Object} state Data state. * @param {string} name Format type name. * + * @example + * ```js + * import { __, sprintf } from '@wordpress/i18n'; + * import { store as richTextStore } from '@wordpress/rich-text'; + * import { useSelect } from '@wordpress/data'; + * + * const ExampleComponent = () => { + * const { getFormatType } = useSelect( + * ( select ) => select( richTextStore ), + * [] + * ); + * + * const boldFormat = getFormatType( 'core/bold' ); + * + * return boldFormat ? ( + *
    + * { Object.entries( boldFormat )?.map( ( [ key, value ] ) => ( + *
  • + * { key } : { value } + *
  • + * ) ) } + *
+ * ) : ( + * __( 'Not Found' ) + * ; + * }; + * ``` + * * @return {Object?} Format type. */ export function getFormatType( state, name ) { @@ -34,6 +88,25 @@ export function getFormatType( state, name ) { * @param {Object} state Data state. * @param {string} bareElementTagName The tag name of the element to find a * format type for. + * + * @example + * ```js + * import { __, sprintf } from '@wordpress/i18n'; + * import { store as richTextStore } from '@wordpress/rich-text'; + * import { useSelect } from '@wordpress/data'; + * + * const ExampleComponent = () => { + * const { getFormatTypeForBareElement } = useSelect( + * ( select ) => select( richTextStore ), + * [] + * ); + * + * const format = getFormatTypeForBareElement( 'strong' ); + * + * return format &&

{ sprintf( __( 'Format name: %s' ), format.name ) }

; + * } + * ``` + * * @return {?Object} Format type. */ export function getFormatTypeForBareElement( state, bareElementTagName ) { @@ -54,6 +127,25 @@ export function getFormatTypeForBareElement( state, bareElementTagName ) { * @param {Object} state Data state. * @param {string} elementClassName The classes of the element to find a format * type for. + * + * @example + * ```js + * import { __, sprintf } from '@wordpress/i18n'; + * import { store as richTextStore } from '@wordpress/rich-text'; + * import { useSelect } from '@wordpress/data'; + * + * const ExampleComponent = () => { + * const { getFormatTypeForClassName } = useSelect( + * ( select ) => select( richTextStore ), + * [] + * ); + * + * const format = getFormatTypeForClassName( 'has-inline-color' ); + * + * return format &&

{ sprintf( __( 'Format name: %s' ), format.name ) }

; + * }; + * ``` + * * @return {?Object} Format type. */ export function getFormatTypeForClassName( state, elementClassName ) { From 10d927c2654cadf850fd1930cd03090cbab1cd0a Mon Sep 17 00:00:00 2001 From: Artemio Morales Date: Thu, 29 Jun 2023 15:30:14 -0500 Subject: [PATCH 090/266] Image block: Fix responsive sizing in lightbox (#51823) * Rewrite logic to calculate image dimensions based on aspect ratio * Add padding for lightbox images using fade animation * Use window inner dimensions to account for mobile address bar On mobile, the address bar is sometimes visible in browsers, which the CSS vh (viewport height) value does not account for. This causes calculations based on vh to render incorrectly if the address bar is ever visible - in this case, placing the lightbox image off center. To address this, I changed the lightbox calculations to be based on window.innerHeight and window.innerWidth instead. * Revise to deactivate responsive image on lightbox close * Use built-in directive for mouseover event * Add safe area inset to close button positioning * Revert "Use built-in directive for mouseover event" This reverts commit a4c9a22ccd17fc92f5dd99796b52605654ce82e9. * Update tests --- packages/block-library/src/image/style.scss | 16 ++++- .../src/image/view-interactivity.js | 61 ++++++++++--------- test/e2e/specs/editor/blocks/image.spec.js | 16 ++++- 3 files changed, 60 insertions(+), 33 deletions(-) diff --git a/packages/block-library/src/image/style.scss b/packages/block-library/src/image/style.scss index d56e56933c6b55..d14f6491a32d4c 100644 --- a/packages/block-library/src/image/style.scss +++ b/packages/block-library/src/image/style.scss @@ -187,8 +187,8 @@ .close-button { position: absolute; - top: 12.5px; - right: 12.5px; + top: calc(env(safe-area-inset-top) + 12.5px); + right: calc(env(safe-area-inset-right) + 12.5px); padding: 0; cursor: pointer; z-index: 5000000; @@ -231,6 +231,18 @@ } &.fade { + .wp-block-image { + padding: 40px 0; + + @media screen and (min-width: 480px) { + padding: 40px; + } + + @media screen and (min-width: 1920px) { + padding: 40px 80px; + } + } + &.active { visibility: visible; animation: both turn-on-visibility 0.25s; diff --git a/packages/block-library/src/image/view-interactivity.js b/packages/block-library/src/image/view-interactivity.js index 82406908a56e0a..e5b8858f10205a 100644 --- a/packages/block-library/src/image/view-interactivity.js +++ b/packages/block-library/src/image/view-interactivity.js @@ -143,7 +143,8 @@ store( { return context.core.image.lightboxEnabled ? 'dialog' : ''; }, responsiveImgSrc: ( { context } ) => { - return context.core.image.activateLargeImage + return context.core.image.activateLargeImage && + context.core.image.hideAnimationEnabled ? '' : context.core.image.imageCurrentSrc; }, @@ -206,48 +207,48 @@ function setZoomStyles( imgDom, context, event ) { let targetWidth = context.core.image.targetWidth; let targetHeight = context.core.image.targetHeight; - const verticalPadding = 40; + // Since the lightbox image has `position:absolute`, it + // ignores its parent's padding, so we need to set padding here + // to calculate dimensions and positioning. - // As per the design, let's allow the image to stretch - // to the full width of its containing figure, but for the height, - // constrain it with a fixed padding - const containerWidth = context.core.image.figureRef.clientWidth; + // As per the design, let's constrain the height with fixed padding + const containerOuterHeight = window.innerHeight; + const verticalPadding = 40; + const containerInnerHeight = containerOuterHeight - verticalPadding * 2; - // The lightbox image has `positione:absolute` and - // ignores its parent's padding, so let's set the padding here, - // to be used when calculating the image width and positioning + // Let's set a variable horizontal padding based on the container width + const containerOuterWidth = window.innerWidth; let horizontalPadding = 0; - if ( containerWidth > 480 ) { + if ( containerOuterWidth > 480 ) { horizontalPadding = 40; - } else if ( containerWidth > 1920 ) { + } else if ( containerOuterWidth > 1920 ) { horizontalPadding = 80; } - - const containerHeight = - context.core.image.figureRef.clientHeight - verticalPadding * 2; + const containerInnerWidth = containerOuterWidth - horizontalPadding * 2; // Check difference between the image and figure dimensions const widthOverflow = Math.abs( - Math.min( containerWidth - targetWidth, 0 ) + Math.min( containerInnerWidth - targetWidth, 0 ) ); const heightOverflow = Math.abs( - Math.min( containerHeight - targetHeight, 0 ) + Math.min( containerInnerHeight - targetHeight, 0 ) ); - // If image is larger than its container any dimension, resize along its largest axis. - // For vertically oriented devices, always maximize the width. + // If the image is larger than the container, let's resize + // it along the greater axis relative to the container if ( widthOverflow > 0 || heightOverflow > 0 ) { - if ( - widthOverflow >= heightOverflow || - containerHeight >= containerWidth - ) { - targetWidth = containerWidth - horizontalPadding * 2; + const containerInnerAspectRatio = + containerInnerWidth / containerInnerHeight; + const imageAspectRatio = targetWidth / targetHeight; + + if ( imageAspectRatio > containerInnerAspectRatio ) { + targetWidth = containerInnerWidth; targetHeight = - imgDom.naturalHeight * ( targetWidth / imgDom.naturalWidth ); + ( targetWidth * imgDom.naturalHeight ) / imgDom.naturalWidth; } else { - targetHeight = containerHeight; + targetHeight = containerInnerHeight; targetWidth = - imgDom.naturalWidth * ( targetHeight / imgDom.naturalHeight ); + ( targetHeight * imgDom.naturalWidth ) / imgDom.naturalHeight; } } @@ -261,16 +262,16 @@ function setZoomStyles( imgDom, context, event ) { // Get values used to center the image let targetLeft = 0; - if ( targetWidth >= containerWidth ) { + if ( targetWidth >= containerInnerWidth ) { targetLeft = horizontalPadding; } else { - targetLeft = ( containerWidth - targetWidth ) / 2; + targetLeft = ( containerOuterWidth - targetWidth ) / 2; } let targetTop = 0; - if ( targetHeight >= containerHeight ) { + if ( targetHeight >= containerInnerHeight ) { targetTop = verticalPadding; } else { - targetTop = ( containerHeight - targetHeight ) / 2 + verticalPadding; + targetTop = ( containerOuterHeight - targetHeight ) / 2; } const root = document.documentElement; diff --git a/test/e2e/specs/editor/blocks/image.spec.js b/test/e2e/specs/editor/blocks/image.spec.js index 242aae941411f9..b392b39de407cf 100644 --- a/test/e2e/specs/editor/blocks/image.spec.js +++ b/test/e2e/specs/editor/blocks/image.spec.js @@ -853,7 +853,10 @@ test.describe( 'Image - interactivity', () => { await page.getByRole( 'button', { name: 'Enlarge image' } ).click(); - await expect( responsiveImage ).toHaveAttribute( 'src', '' ); + await expect( responsiveImage ).toHaveAttribute( + 'src', + new RegExp( filename ) + ); await expect( enlargedImage ).toHaveAttribute( 'src', new RegExp( filename ) @@ -866,6 +869,12 @@ test.describe( 'Image - interactivity', () => { } ); await closeButton.click(); + await expect( responsiveImage ).toHaveAttribute( 'src', '' ); + await expect( enlargedImage ).toHaveAttribute( + 'src', + new RegExp( filename ) + ); + await expect( lightbox ).toBeHidden(); } ); @@ -1093,6 +1102,11 @@ test.describe( 'Image - interactivity', () => { await page.getByRole( 'button', { name: 'Enlarge image' } ).click(); + await expect( responsiveImage ).toHaveAttribute( 'src', imgUrl ); + await expect( enlargedImage ).toHaveAttribute( 'src', imgUrl ); + + await page.getByRole( 'button', { name: 'Close' } ).click(); + await expect( responsiveImage ).toHaveAttribute( 'src', '' ); await expect( enlargedImage ).toHaveAttribute( 'src', imgUrl ); } ); From b2b968dce5d9740f267cf5a974b12d5165eec7f9 Mon Sep 17 00:00:00 2001 From: Ramon Date: Fri, 30 Jun 2023 08:27:13 +1000 Subject: [PATCH 091/266] backporting from https://github.com/WordPress/wordpress-develop/pull/4606 (#52095) --- ...berg-rest-global-styles-controller-6-3.php | 2 +- ...global-styles-revisions-controller-6-3.php | 193 ++++- ...lobal-styles-revisions-controller-test.php | 724 ++++++++++++++++-- 3 files changed, 821 insertions(+), 98 deletions(-) diff --git a/lib/compat/wordpress-6.3/class-gutenberg-rest-global-styles-controller-6-3.php b/lib/compat/wordpress-6.3/class-gutenberg-rest-global-styles-controller-6-3.php index 09b0f4ec65dbf6..ed4dac70e85a54 100644 --- a/lib/compat/wordpress-6.3/class-gutenberg-rest-global-styles-controller-6-3.php +++ b/lib/compat/wordpress-6.3/class-gutenberg-rest-global-styles-controller-6-3.php @@ -31,7 +31,7 @@ protected function prepare_links( $id ) { if ( post_type_supports( $this->post_type, 'revisions' ) ) { $revisions = wp_get_latest_revision_id_and_total_count( $id ); $revisions_count = ! is_wp_error( $revisions ) ? $revisions['count'] : 0; - $revisions_base = sprintf( '/%s/%s/%d/revisions', $this->namespace, $this->rest_base, $id ); + $revisions_base = sprintf( '/%s/%d/revisions', $base, $id ); $links['version-history'] = array( 'href' => rest_url( $revisions_base ), 'count' => $revisions_count, diff --git a/lib/compat/wordpress-6.3/class-gutenberg-rest-global-styles-revisions-controller-6-3.php b/lib/compat/wordpress-6.3/class-gutenberg-rest-global-styles-revisions-controller-6-3.php index 356b435b71f1e8..c45ce23c5d4ea7 100644 --- a/lib/compat/wordpress-6.3/class-gutenberg-rest-global-styles-revisions-controller-6-3.php +++ b/lib/compat/wordpress-6.3/class-gutenberg-rest-global-styles-revisions-controller-6-3.php @@ -63,12 +63,59 @@ public function register_routes() { 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_items' ), 'permission_callback' => array( $this, 'get_item_permissions_check' ), + 'args' => $this->get_collection_params(), ), 'schema' => array( $this, 'get_public_item_schema' ), ) ); } + /** + * Retrieves the query params for collections. + * + * Inherits from WP_REST_Controller::get_collection_params(), + * also reflects changes to return value WP_REST_Revisions_Controller::get_collection_params(). + * + * @since 6.3.0 + * + * @return array Collection parameters. + */ + public function get_collection_params() { + $collection_params = parent::get_collection_params(); + $collection_params['context']['default'] = 'view'; + $collection_params['offset'] = array( + 'description' => __( 'Offset the result set by a specific number of items.' ), + 'type' => 'integer', + ); + unset( $collection_params['search'] ); + unset( $collection_params['per_page']['default'] ); + + return $collection_params; + } + + /** + * Returns decoded JSON from post content string, + * or a 404 if not found. + * + * @since 6.3.0 + * + * @param string $raw_json Encoded JSON from global styles custom post content. + * @return Array|WP_Error + */ + private function get_decoded_global_styles_json( $raw_json ) { + $decoded_json = json_decode( $raw_json, true ); + + if ( is_array( $decoded_json ) && isset( $decoded_json['isGlobalStylesUserThemeJSON'] ) && true === $decoded_json['isGlobalStylesUserThemeJSON'] ) { + return $decoded_json; + } + + return new WP_Error( + 'rest_global_styles_not_found', + __( 'Cannot find user global styles revisions.' ), + array( 'status' => 404 ) + ); + } + /** * Returns revisions of the given global styles config custom post type. * @@ -84,27 +131,112 @@ public function get_items( $request ) { if ( is_wp_error( $parent ) ) { return $parent; } - $response = array(); - $raw_config = json_decode( $parent->post_content, true ); - $is_global_styles_user_theme_json = isset( $raw_config['isGlobalStylesUserThemeJSON'] ) && true === $raw_config['isGlobalStylesUserThemeJSON']; - if ( $is_global_styles_user_theme_json ) { - $user_theme_revisions = wp_get_post_revisions( - $parent->ID, - array( - 'posts_per_page' => 100, - ) + $global_styles_config = $this->get_decoded_global_styles_json( $parent->post_content ); + + if ( is_wp_error( $global_styles_config ) ) { + return $global_styles_config; + } + + if ( wp_revisions_enabled( $parent ) ) { + $registered = $this->get_collection_params(); + $query_args = array( + 'post_parent' => $parent->ID, + 'post_type' => 'revision', + 'post_status' => 'inherit', + 'posts_per_page' => -1, + 'orderby' => 'date ID', + 'order' => 'DESC', + ); + + $parameter_mappings = array( + 'offset' => 'offset', + 'page' => 'paged', + 'per_page' => 'posts_per_page', ); - if ( ! empty( $user_theme_revisions ) ) { - foreach ( $user_theme_revisions as $revision ) { - $revision = $this->prepare_item_for_response( $revision, $request ); - $response[] = $this->prepare_response_for_collection( $revision ); + foreach ( $parameter_mappings as $api_param => $wp_param ) { + if ( isset( $registered[ $api_param ], $request[ $api_param ] ) ) { + $query_args[ $wp_param ] = $request[ $api_param ]; + } + } + + $revisions_query = new WP_Query(); + $revisions = $revisions_query->query( $query_args ); + $offset = isset( $query_args['offset'] ) ? (int) $query_args['offset'] : 0; + $page = (int) $query_args['paged']; + $total_revisions = $revisions_query->found_posts; + + if ( $total_revisions < 1 ) { + // Out-of-bounds, run the query again without LIMIT for total count. + unset( $query_args['paged'], $query_args['offset'] ); + $count_query = new WP_Query(); + $count_query->query( $query_args ); + + $total_revisions = $count_query->found_posts; + } + + if ( $revisions_query->query_vars['posts_per_page'] > 0 ) { + $max_pages = ceil( $total_revisions / (int) $revisions_query->query_vars['posts_per_page'] ); + } else { + $max_pages = $total_revisions > 0 ? 1 : 0; + } + if ( $total_revisions > 0 ) { + if ( $offset >= $total_revisions ) { + return new WP_Error( + 'rest_revision_invalid_offset_number', + __( 'The offset number requested is larger than or equal to the number of available revisions.', 'gutenberg' ), + array( 'status' => 400 ) + ); + } elseif ( ! $offset && $page > $max_pages ) { + return new WP_Error( + 'rest_revision_invalid_page_number', + __( 'The page number requested is larger than the number of pages available.', 'gutenberg' ), + array( 'status' => 400 ) + ); } } + } else { + $revisions = array(); + $total_revisions = 0; + $max_pages = 0; + $page = (int) $request['page']; + } + + $response = array(); + + foreach ( $revisions as $revision ) { + $data = $this->prepare_item_for_response( $revision, $request ); + $response[] = $this->prepare_response_for_collection( $data ); + } + + $response = rest_ensure_response( $response ); + + $response->header( 'X-WP-Total', (int) $total_revisions ); + $response->header( 'X-WP-TotalPages', (int) $max_pages ); + + $request_params = $request->get_query_params(); + $base_path = rest_url( sprintf( '%s/%s/%d/%s', $this->namespace, $this->parent_base, $request['parent'], $this->rest_base ) ); + $base = add_query_arg( urlencode_deep( $request_params ), $base_path ); + + if ( $page > 1 ) { + $prev_page = $page - 1; + + if ( $prev_page > $max_pages ) { + $prev_page = $max_pages; + } + + $prev_link = add_query_arg( 'page', $prev_page, $base ); + $response->link_header( 'prev', $prev_link ); } + if ( $max_pages > $page ) { + $next_page = $page + 1; + $next_link = add_query_arg( 'page', $next_page, $base ); - return rest_ensure_response( $response ); + $response->link_header( 'next', $next_link ); + } + + return $response; } /** @@ -137,17 +269,28 @@ protected function prepare_date_response( $date_gmt, $date = null ) { * * @param WP_Post $post Post revision object. * @param WP_REST_Request $request Request object. - * @return WP_REST_Response Response object. + * @return WP_REST_Response|WP_Error Response object. */ public function prepare_item_for_response( $post, $request ) { - $parent = $this->get_parent( $request['parent'] ); - // Retrieves global styles config as JSON. - $raw_revision_config = json_decode( $post->post_content, true ); - $config = ( new WP_Theme_JSON_Gutenberg( $raw_revision_config, 'custom' ) )->get_raw_data(); + $parent = $this->get_parent( $request['parent'] ); + $global_styles_config = $this->get_decoded_global_styles_json( $post->post_content ); + + if ( is_wp_error( $global_styles_config ) ) { + return $global_styles_config; + } - // Prepares item data. - $data = array(); $fields = $this->get_fields_for_response( $request ); + $data = array(); + + if ( ! empty( $global_styles_config['styles'] ) || ! empty( $global_styles_config['settings'] ) ) { + $global_styles_config = ( new WP_Theme_JSON_Gutenberg( $global_styles_config, 'custom' ) )->get_raw_data(); + if ( rest_is_field_included( 'settings', $fields ) ) { + $data['settings'] = ! empty( $global_styles_config['settings'] ) ? $global_styles_config['settings'] : new stdClass(); + } + if ( rest_is_field_included( 'styles', $fields ) ) { + $data['styles'] = ! empty( $global_styles_config['styles'] ) ? $global_styles_config['styles'] : new stdClass(); + } + } if ( rest_is_field_included( 'author', $fields ) ) { $data['author'] = (int) $post->post_author; @@ -177,14 +320,6 @@ public function prepare_item_for_response( $post, $request ) { $data['parent'] = (int) $parent->ID; } - if ( rest_is_field_included( 'settings', $fields ) ) { - $data['settings'] = ! empty( $config['settings'] ) ? $config['settings'] : new stdClass(); - } - - if ( rest_is_field_included( 'styles', $fields ) ) { - $data['styles'] = ! empty( $config['styles'] ) ? $config['styles'] : new stdClass(); - } - $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; $data = $this->add_additional_fields_to_object( $data, $request ); $data = $this->filter_response_by_context( $data, $context ); diff --git a/phpunit/class-gutenberg-rest-global-styles-revisions-controller-test.php b/phpunit/class-gutenberg-rest-global-styles-revisions-controller-test.php index 3e7f8763c5c765..7e95c2c26c8bec 100644 --- a/phpunit/class-gutenberg-rest-global-styles-revisions-controller-test.php +++ b/phpunit/class-gutenberg-rest-global-styles-revisions-controller-test.php @@ -21,10 +21,40 @@ class Gutenberg_REST_Global_Styles_Revisions_Controller_Test extends WP_Test_RES */ protected static $global_styles_id; - public function set_up() { - parent::set_up(); - switch_theme( 'emptytheme' ); - } + /** + * @var int + */ + private $total_revisions; + + /** + * @var array + */ + private $revision_1; + + /** + * @var int + */ + private $revision_1_id; + + /** + * @var array + */ + private $revision_2; + + /** + * @var int + */ + private $revision_2_id; + + /** + * @var array + */ + private $revision_3; + + /** + * @var int + */ + private $revision_3_id; /** * Create fake data before our tests run. @@ -47,19 +77,113 @@ public static function wpSetupBeforeClass( $factory ) { 'role' => 'author', ) ); + + wp_set_current_user( self::$admin_id ); // This creates the global styles for the current theme. self::$global_styles_id = $factory->post->create( array( - 'post_content' => '{"version": ' . WP_Theme_JSON_Gutenberg::LATEST_SCHEMA . ', "isGlobalStylesUserThemeJSON": true }', + 'post_content' => '{"version": ' . WP_Theme_JSON::LATEST_SCHEMA . ', "isGlobalStylesUserThemeJSON": true }', 'post_status' => 'publish', 'post_title' => __( 'Custom Styles', 'default' ), 'post_type' => 'wp_global_styles', - 'post_name' => 'wp-global-styles-emptytheme-revisions', + 'post_name' => 'wp-global-styles-tt1-blocks-revisions', 'tax_input' => array( - 'wp_theme' => 'emptytheme', + 'wp_theme' => 'tt1-blocks', ), ) ); + + // Update post to create a new revisions. + $new_styles_post = array( + 'ID' => self::$global_styles_id, + 'post_content' => wp_json_encode( + array( + 'version' => WP_Theme_JSON::LATEST_SCHEMA, + 'isGlobalStylesUserThemeJSON' => true, + 'styles' => array( + 'color' => array( + 'background' => 'hotpink', + ), + ), + 'settings' => array( + 'color' => array( + 'palette' => array( + 'custom' => array( + array( + 'name' => 'Ghost', + 'slug' => 'ghost', + 'color' => 'ghost', + ), + ), + ), + ), + ), + ) + ), + ); + + wp_update_post( $new_styles_post, true, false ); + + $new_styles_post = array( + 'ID' => self::$global_styles_id, + 'post_content' => wp_json_encode( + array( + 'version' => WP_Theme_JSON::LATEST_SCHEMA, + 'isGlobalStylesUserThemeJSON' => true, + 'styles' => array( + 'color' => array( + 'background' => 'lemonchiffon', + ), + ), + 'settings' => array( + 'color' => array( + 'palette' => array( + 'custom' => array( + array( + 'name' => 'Gwanda', + 'slug' => 'gwanda', + 'color' => 'gwanda', + ), + ), + ), + ), + ), + ) + ), + ); + + wp_update_post( $new_styles_post, true, false ); + + $new_styles_post = array( + 'ID' => self::$global_styles_id, + 'post_content' => wp_json_encode( + array( + 'version' => WP_Theme_JSON::LATEST_SCHEMA, + 'isGlobalStylesUserThemeJSON' => true, + 'styles' => array( + 'color' => array( + 'background' => 'chocolate', + ), + ), + 'settings' => array( + 'color' => array( + 'palette' => array( + 'custom' => array( + array( + 'name' => 'Stacy', + 'slug' => 'stacy', + 'color' => 'stacy', + ), + ), + ), + ), + ), + ) + ), + ); + + wp_update_post( $new_styles_post, true, false ); + wp_set_current_user( 0 ); } /** @@ -72,114 +196,184 @@ public static function wpTearDownAfterClass() { } /** - * @covers Gutenberg_REST_Global_Styles_Revisions_Controller::register_routes + * Sets up before tests. + */ + public function set_up() { + parent::set_up(); + switch_theme( 'emptytheme' ); + $revisions = wp_get_post_revisions( self::$global_styles_id ); + $this->total_revisions = count( $revisions ); + + $this->revision_1 = array_pop( $revisions ); + $this->revision_1_id = $this->revision_1->ID; + + $this->revision_2 = array_pop( $revisions ); + $this->revision_2_id = $this->revision_2->ID; + + $this->revision_3 = array_pop( $revisions ); + $this->revision_3_id = $this->revision_3->ID; + } + + /** + * @ticket 58524 + * + * @covers WP_REST_Global_Styles_Controller::register_routes */ public function test_register_routes() { $routes = rest_get_server()->get_routes(); $this->assertArrayHasKey( '/wp/v2/global-styles/(?P[\d]+)/revisions', $routes, - 'Global style revisions based on the given parentID route does not exist' + 'Global style revisions based on the given parentID route does not exist.' ); } /** - * @covers Gutenberg_REST_Global_Styles_Revisions_Controller::get_items + * @ticket 58524 + * + * @covers WP_REST_Global_Styles_Controller::get_items */ - public function test_get_items() { + public function test_get_items_missing_parent() { wp_set_current_user( self::$admin_id ); - // Update post to create a new revision. - $config = array( - 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, - 'isGlobalStylesUserThemeJSON' => true, - 'styles' => array( - 'color' => array( - 'background' => 'hotpink', - ), - ), + $request = new WP_REST_Request( 'GET', '/wp/v2/global-styles/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER . '/revisions' ); + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 ); + } + + /** + * Utility function to check the items in WP_REST_Global_Styles_Controller::get_items + * against the expected values. + * + * @ticket 58524 + */ + protected function check_get_revision_response( $response_revision_item, $revision_expected_item ) { + $this->assertSame( (int) $revision_expected_item->post_author, $response_revision_item['author'], 'Check that the revision item `author` exists.' ); + $this->assertSame( mysql_to_rfc3339( $revision_expected_item->post_date ), $response_revision_item['date'], 'Check that the revision item `date` exists.' ); + $this->assertSame( mysql_to_rfc3339( $revision_expected_item->post_date_gmt ), $response_revision_item['date_gmt'], 'Check that the revision item `date_gmt` exists.' ); + $this->assertSame( mysql_to_rfc3339( $revision_expected_item->post_modified ), $response_revision_item['modified'], 'Check that the revision item `modified` exists.' ); + $this->assertSame( mysql_to_rfc3339( $revision_expected_item->post_modified_gmt ), $response_revision_item['modified_gmt'], 'Check that the revision item `modified_gmt` exists.' ); + $this->assertSame( $revision_expected_item->post_parent, $response_revision_item['parent'], 'Check that an id for the parent exists.' ); + + // Global styles. + $config = ( new WP_Theme_JSON( json_decode( $revision_expected_item->post_content, true ), 'custom' ) )->get_raw_data(); + $this->assertEquals( + $config['settings'], + $response_revision_item['settings'], + 'Check that the revision settings exist in the response.' ); - $new_styles_post = array( - 'ID' => self::$global_styles_id, - 'post_content' => wp_json_encode( $config ), + $this->assertEquals( + $config['styles'], + $response_revision_item['styles'], + 'Check that the revision styles match the updated styles.' ); + } - wp_update_post( $new_styles_post, true, false ); + /** + * @ticket 58524 + * + * @covers WP_REST_Global_Styles_Controller::get_items + */ + public function test_get_items() { + wp_set_current_user( self::$admin_id ); $request = new WP_REST_Request( 'GET', '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' ); $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); - $this->assertCount( 1, $data, 'Check that only one revision exists' ); - $this->assertArrayHasKey( 'id', $data[0], 'Check that an id key exists' ); - $this->assertEquals( self::$global_styles_id, $data[0]['parent'], 'Check that an id for the parent exists' ); + $this->assertSame( 200, $response->get_status(), 'Response status is 200.' ); + $this->assertCount( $this->total_revisions, $data, 'Check that correct number of revisions exists.' ); - // Dates. - $this->assertArrayHasKey( 'date', $data[0], 'Check that an date key exists' ); - $this->assertArrayHasKey( 'date_gmt', $data[0], 'Check that an date_gmt key exists' ); - $this->assertArrayHasKey( 'modified', $data[0], 'Check that an modified key exists' ); - $this->assertArrayHasKey( 'modified_gmt', $data[0], 'Check that an modified_gmt key exists' ); - $this->assertArrayHasKey( 'modified_gmt', $data[0], 'Check that an modified_gmt key exists' ); + // Reverse chronology. + $this->assertSame( $this->revision_3_id, $data[0]['id'] ); + $this->check_get_revision_response( $data[0], $this->revision_3 ); - // Author information. - $this->assertEquals( self::$admin_id, $data[0]['author'], 'Check that author id returns expected value' ); + $this->assertSame( $this->revision_2_id, $data[1]['id'] ); + $this->check_get_revision_response( $data[1], $this->revision_2 ); - // Global styles. - $this->assertEquals( - $data[0]['settings'], - new stdClass(), - 'Check that the revision settings exist in the response.' - ); - $this->assertEquals( - $data[0]['styles'], - array( + $this->assertSame( $this->revision_1_id, $data[2]['id'] ); + $this->check_get_revision_response( $data[2], $this->revision_1 ); + } + + /** + * @ticket 58524 + * + * @covers WP_REST_Global_Styles_Controller::get_items + */ + public function test_get_items_eligible_roles() { + wp_set_current_user( self::$second_admin_id ); + $config = array( + 'version' => WP_Theme_JSON::LATEST_SCHEMA, + 'isGlobalStylesUserThemeJSON' => true, + 'styles' => array( 'color' => array( - 'background' => 'hotpink', + 'background' => 'whitesmoke', ), ), - 'Check that the revision styles match the last updated styles.' + 'settings' => array(), ); - - // Checks that the revisions are returned for all eligible users. - wp_set_current_user( self::$second_admin_id ); - $config['styles']['color']['background'] = 'blue'; - $new_styles_post = array( + $updated_styles_post = array( 'ID' => self::$global_styles_id, 'post_content' => wp_json_encode( $config ), ); - wp_update_post( $new_styles_post, true, false ); + wp_update_post( $updated_styles_post, true, false ); $request = new WP_REST_Request( 'GET', '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' ); $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); - $this->assertCount( 2, $data, 'Check that two revisions exists' ); - $this->assertEquals( self::$second_admin_id, $data[0]['author'], 'Check that second author id returns expected value' ); - $this->assertEquals( self::$admin_id, $data[1]['author'], 'Check that second author id returns expected value' ); + $this->assertCount( $this->total_revisions + 1, $data, 'Check that extra revision exist' ); + $this->assertEquals( self::$second_admin_id, $data[0]['author'], 'Check that second author id returns expected value.' ); } /** - * @covers Gutenberg_REST_Global_Styles_Revisions_Controller::get_item_schema + * @ticket 58524 + * + * @covers WP_REST_Global_Styles_Controller::get_items with context arg. + */ + public function test_get_item_embed_context() { + wp_set_current_user( self::$admin_id ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' ); + $request->set_param( 'context', 'embed' ); + $response = rest_get_server()->dispatch( $request ); + $fields = array( + 'author', + 'date', + 'id', + 'parent', + ); + $data = $response->get_data(); + $this->assertSameSets( $fields, array_keys( $data[0] ) ); + } + + /** + * @ticket 58524 + * + * @covers WP_REST_Global_Styles_Controller::get_item_schema */ public function test_get_item_schema() { $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' ); $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); $properties = $data['schema']['properties']; - $this->assertCount( 9, $properties, 'Schema properties array does not have exactly 9 elements' ); - $this->assertArrayHasKey( 'id', $properties, 'Schema properties array does not have "id" key' ); - $this->assertArrayHasKey( 'styles', $properties, 'Schema properties array does not have "styles" key' ); - $this->assertArrayHasKey( 'settings', $properties, 'Schema properties array does not have "settings" key' ); - $this->assertArrayHasKey( 'parent', $properties, 'Schema properties array does not have "parent" key' ); - $this->assertArrayHasKey( 'author', $properties, 'Schema properties array does not have "author" key' ); - $this->assertArrayHasKey( 'date', $properties, 'Schema properties array does not have "date" key' ); - $this->assertArrayHasKey( 'date_gmt', $properties, 'Schema properties array does not have "date_gmt" key' ); - $this->assertArrayHasKey( 'modified', $properties, 'Schema properties array does not have "modified" key' ); - $this->assertArrayHasKey( 'modified_gmt', $properties, 'Schema properties array does not have "modified_gmt" key' ); + + $this->assertCount( 9, $properties, 'Schema properties array has exactly 9 elements.' ); + $this->assertArrayHasKey( 'id', $properties, 'Schema properties array has "id" key.' ); + $this->assertArrayHasKey( 'styles', $properties, 'Schema properties array has "styles" key.' ); + $this->assertArrayHasKey( 'settings', $properties, 'Schema properties array has "settings" key.' ); + $this->assertArrayHasKey( 'parent', $properties, 'Schema properties array has "parent" key.' ); + $this->assertArrayHasKey( 'author', $properties, 'Schema properties array has "author" key.' ); + $this->assertArrayHasKey( 'date', $properties, 'Schema properties array has "date" key.' ); + $this->assertArrayHasKey( 'date_gmt', $properties, 'Schema properties array has "date_gmt" key.' ); + $this->assertArrayHasKey( 'modified', $properties, 'Schema properties array has "modified" key.' ); + $this->assertArrayHasKey( 'modified_gmt', $properties, 'Schema properties array has "modified_gmt" key.' ); } /** - * @covers Gutenberg_REST_Global_Styles_Revisions_Controller::get_item_permissions_check + * @ticket 58524 + * + * @covers WP_REST_Global_Styles_Controller::get_item_permissions_check */ public function test_get_item_permissions_check() { wp_set_current_user( self::$author_id ); @@ -189,11 +383,405 @@ public function test_get_item_permissions_check() { $this->assertErrorResponse( 'rest_cannot_view', $response, 403 ); } + /** + * Tests the pagination header of the first page. + * + * Duplicate of WP_Test_REST_Revisions_Controller::test_get_items_pagination_header_of_the_first_page + * + * @ticket 58524 + * + * @covers WP_REST_Global_Styles_Controller::get_items + */ + public function test_get_items_pagination_header_of_the_first_page() { + wp_set_current_user( self::$admin_id ); + + $rest_route = '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions'; + $per_page = 2; + $total_pages = (int) ceil( $this->total_revisions / $per_page ); + $page = 1; // First page. + + $request = new WP_REST_Request( 'GET', $rest_route ); + $request->set_query_params( + array( + 'per_page' => $per_page, + 'page' => $page, + ) + ); + $response = rest_get_server()->dispatch( $request ); + $headers = $response->get_headers(); + $this->assertSame( $this->total_revisions, $headers['X-WP-Total'] ); + $this->assertSame( $total_pages, $headers['X-WP-TotalPages'] ); + $next_link = add_query_arg( + array( + 'per_page' => $per_page, + 'page' => $page + 1, + ), + rest_url( $rest_route ) + ); + $this->assertStringNotContainsString( 'rel="prev"', $headers['Link'] ); + $this->assertStringContainsString( '<' . $next_link . '>; rel="next"', $headers['Link'] ); + } + + /** + * Tests the pagination header of the last page. + * + * Duplicate of WP_Test_REST_Revisions_Controller::test_get_items_pagination_header_of_the_last_page + * + * @ticket 58524 + * + * @covers WP_REST_Global_Styles_Controller::get_items + */ + public function test_get_items_pagination_header_of_the_last_page() { + wp_set_current_user( self::$admin_id ); + + $rest_route = '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions'; + $per_page = 2; + $total_pages = (int) ceil( $this->total_revisions / $per_page ); + $page = 2; // Last page. + + $request = new WP_REST_Request( 'GET', $rest_route ); + $request->set_query_params( + array( + 'per_page' => $per_page, + 'page' => $page, + ) + ); + $response = rest_get_server()->dispatch( $request ); + $headers = $response->get_headers(); + $this->assertSame( $this->total_revisions, $headers['X-WP-Total'] ); + $this->assertSame( $total_pages, $headers['X-WP-TotalPages'] ); + $prev_link = add_query_arg( + array( + 'per_page' => $per_page, + 'page' => $page - 1, + ), + rest_url( $rest_route ) + ); + $this->assertStringContainsString( '<' . $prev_link . '>; rel="prev"', $headers['Link'] ); + } + + /** + * Tests that invalid 'per_page' query should error. + * + * Duplicate of WP_Test_REST_Revisions_Controller::test_get_items_invalid_per_page_should_error + * + * @ticket 58524 + * + * @covers WP_REST_Global_Styles_Controller::get_items + */ + public function test_get_items_invalid_per_page_should_error() { + wp_set_current_user( self::$admin_id ); + + $per_page = -1; // Invalid number. + $expected_error = 'rest_invalid_param'; + $expected_status = 400; + + $request = new WP_REST_Request( 'GET', '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' ); + $request->set_param( 'per_page', $per_page ); + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( $expected_error, $response, $expected_status ); + } + + /** + * Tests that out of bounds 'page' query should error. + * + * Duplicate of WP_Test_REST_Revisions_Controller::test_get_items_out_of_bounds_page_should_error + * + * @ticket 58524 + * + * @covers WP_REST_Global_Styles_Controller::get_items + */ + public function test_get_items_out_of_bounds_page_should_error() { + wp_set_current_user( self::$admin_id ); + + $per_page = 2; + $total_pages = (int) ceil( $this->total_revisions / $per_page ); + $page = $total_pages + 1; // Out of bound page. + $expected_error = 'rest_revision_invalid_page_number'; + $expected_status = 400; + + $request = new WP_REST_Request( 'GET', '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' ); + $request->set_query_params( + array( + 'per_page' => $per_page, + 'page' => $page, + ) + ); + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( $expected_error, $response, $expected_status ); + } + + /** + * Tests that impossibly high 'page' query should error. + * + * Duplicate of WP_Test_REST_Revisions_Controller::test_get_items_invalid_max_pages_should_error + * + * @ticket 58524 + * + * @covers WP_REST_Global_Styles_Controller::get_items + */ + public function test_get_items_invalid_max_pages_should_error() { + wp_set_current_user( self::$admin_id ); + + $per_page = 2; + $page = REST_TESTS_IMPOSSIBLY_HIGH_NUMBER; // Invalid number. + $expected_error = 'rest_revision_invalid_page_number'; + $expected_status = 400; + + $request = new WP_REST_Request( 'GET', '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' ); + $request->set_query_params( + array( + 'per_page' => $per_page, + 'page' => $page, + ) + ); + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( $expected_error, $response, $expected_status ); + } + + /** + * Tests that the default query should fetch all revisions. + * + * Duplicate of WP_Test_REST_Revisions_Controller::test_get_items_default_query_should_fetch_all_revisons + * + * @ticket 58524 + * + * @covers WP_REST_Global_Styles_Controller::get_items + */ + public function test_get_items_default_query_should_fetch_all_revisons() { + wp_set_current_user( self::$admin_id ); + + $expected_count = $this->total_revisions; + + $request = new WP_REST_Request( 'GET', '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' ); + $response = rest_get_server()->dispatch( $request ); + $this->assertCount( $expected_count, $response->get_data() ); + } + + /** + * Tests that 'offset' query shouldn't work without 'per_page' (fallback -1). + * + * Duplicate of WP_Test_REST_Revisions_Controller::test_get_items_offset_should_not_work_without_per_page + * + * @ticket 58524 + * + * @covers WP_REST_Global_Styles_Controller::get_items + */ + public function test_get_items_offset_should_not_work_without_per_page() { + wp_set_current_user( self::$admin_id ); + + $offset = 1; + $expected_count = $this->total_revisions; + + $request = new WP_REST_Request( 'GET', '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' ); + $request->set_param( 'offset', $offset ); + $response = rest_get_server()->dispatch( $request ); + $this->assertCount( $expected_count, $response->get_data() ); + } + + /** + * Tests that 'offset' query should work with 'per_page'. + * + * Duplicate of WP_Test_REST_Revisions_Controller::test_get_items_offset_should_work_with_per_page + * + * @ticket 58524 + * + * @covers WP_REST_Global_Styles_Controller::get_items + */ + public function test_get_items_offset_should_work_with_per_page() { + wp_set_current_user( self::$admin_id ); + + $per_page = 2; + $offset = 1; + $expected_count = 2; + + $request = new WP_REST_Request( 'GET', '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' ); + $request->set_query_params( + array( + 'offset' => $offset, + 'per_page' => $per_page, + ) + ); + $response = rest_get_server()->dispatch( $request ); + $this->assertCount( $expected_count, $response->get_data() ); + } + + /** + * Tests that 'offset' query should take priority over 'page'. + * + * Duplicate of WP_Test_REST_Revisions_Controller::test_get_items_offset_should_take_priority_over_page + * + * @ticket 58524 + * + * @covers WP_REST_Global_Styles_Controller::get_items + */ + public function test_get_items_offset_should_take_priority_over_page() { + wp_set_current_user( self::$admin_id ); + + $per_page = 2; + $offset = 1; + $page = 1; + $expected_count = 2; + + $request = new WP_REST_Request( 'GET', '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' ); + $request->set_query_params( + array( + 'offset' => $offset, + 'per_page' => $per_page, + 'page' => $page, + ) + ); + $response = rest_get_server()->dispatch( $request ); + $this->assertCount( $expected_count, $response->get_data() ); + } + + /** + * Tests that 'offset' query, as the total revisions count, should return empty data. + * + * Duplicate of WP_Test_REST_Revisions_Controller::test_get_items_total_revisions_offset_should_return_empty_data + * + * @ticket 58524 + * + * @covers WP_REST_Global_Styles_Controller::get_items + */ + public function test_get_items_total_revisions_offset_should_return_empty_data() { + wp_set_current_user( self::$admin_id ); + + $per_page = 2; + $offset = $this->total_revisions; + $expected_error = 'rest_revision_invalid_offset_number'; + $expected_status = 400; + + $request = new WP_REST_Request( 'GET', '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' ); + $request->set_query_params( + array( + 'offset' => $offset, + 'per_page' => $per_page, + ) + ); + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( $expected_error, $response, $expected_status ); + } + + /** + * Tests that out of bound 'offset' query should error. + * + * Duplicate of WP_Test_REST_Revisions_Controller::test_get_items_out_of_bound_offset_should_error + * + * @ticket 58524 + * + * @covers WP_REST_Global_Styles_Controller::get_items + */ + public function test_get_items_out_of_bound_offset_should_error() { + wp_set_current_user( self::$admin_id ); + + $per_page = 2; + $offset = $this->total_revisions + 1; + $expected_error = 'rest_revision_invalid_offset_number'; + $expected_status = 400; + + $request = new WP_REST_Request( 'GET', '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' ); + $request->set_query_params( + array( + 'offset' => $offset, + 'per_page' => $per_page, + ) + ); + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( $expected_error, $response, $expected_status ); + } + + /** + * Tests that impossible high number for 'offset' query should error. + * + * Duplicate of WP_Test_REST_Revisions_Controller::test_get_items_impossible_high_number_offset_should_error + * + * @ticket 58524 + * + * @covers WP_REST_Global_Styles_Controller::get_items + */ + public function test_get_items_impossible_high_number_offset_should_error() { + wp_set_current_user( self::$admin_id ); + + $per_page = 2; + $offset = REST_TESTS_IMPOSSIBLY_HIGH_NUMBER; + $expected_error = 'rest_revision_invalid_offset_number'; + $expected_status = 400; + + $request = new WP_REST_Request( 'GET', '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' ); + $request->set_query_params( + array( + 'offset' => $offset, + 'per_page' => $per_page, + ) + ); + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( $expected_error, $response, $expected_status ); + } + + /** + * Tests that invalid 'offset' query should error. + * + * Duplicate of WP_Test_REST_Revisions_Controller::test_get_items_invalid_offset_should_error + * + * @ticket 58524 + * + * @covers WP_REST_Global_Styles_Controller::get_items + */ + public function test_get_items_invalid_offset_should_error() { + wp_set_current_user( self::$admin_id ); + + $per_page = 2; + $offset = 'moreplease'; + $expected_error = 'rest_invalid_param'; + $expected_status = 400; + + $request = new WP_REST_Request( 'GET', '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' ); + $request->set_query_params( + array( + 'offset' => $offset, + 'per_page' => $per_page, + ) + ); + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( $expected_error, $response, $expected_status ); + } + + /** + * Tests that out of bounds 'page' query should not error when offset is provided, + * because it takes precedence. + * + * Duplicate of WP_Test_REST_Revisions_Controller::test_get_items_out_of_bounds_page_should_not_error_if_offset + * + * @ticket 58524 + * + * @covers WP_REST_Global_Styles_Controller::get_items + */ + public function test_get_items_out_of_bounds_page_should_not_error_if_offset() { + wp_set_current_user( self::$admin_id ); + + $per_page = 2; + $total_pages = (int) ceil( $this->total_revisions / $per_page ); + $page = $total_pages + 1; // Out of bound page. + $expected_count = 2; + + $request = new WP_REST_Request( 'GET', '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' ); + $request->set_query_params( + array( + 'offset' => 1, + 'per_page' => $per_page, + 'page' => $page, + ) + ); + $response = rest_get_server()->dispatch( $request ); + $this->assertCount( $expected_count, $response->get_data() ); + } + /** * @doesNotPerformAssertions */ public function test_context_param() { - // Controller does not use get_context_param(). + // Controller does not implement test_context_param(). } /** From a7fb51532e9a1185f9ebe9dde18ab8ab86cb2697 Mon Sep 17 00:00:00 2001 From: Artemio Morales Date: Thu, 29 Jun 2023 21:20:36 -0500 Subject: [PATCH 092/266] Image block: Use built-in directive for mouseover event in lightbox (#52067) * Use built-in directive for mouseover event * Change mouseover directive to mouseenter --- lib/block-supports/behaviors.php | 2 +- .../src/image/view-interactivity.js | 22 +++++++++---------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/lib/block-supports/behaviors.php b/lib/block-supports/behaviors.php index 28b097c8f60449..5f6c72e52199e3 100644 --- a/lib/block-supports/behaviors.php +++ b/lib/block-supports/behaviors.php @@ -141,7 +141,7 @@ function gutenberg_render_behaviors_support_lightbox( $block_content, $block ) { $img = null; preg_match( '/]+>/', $body_content, $img ); $button = '
- ' + ' . $img[0] . '
'; $body_content = preg_replace( '/]+>/', $button, $body_content ); diff --git a/packages/block-library/src/image/view-interactivity.js b/packages/block-library/src/image/view-interactivity.js index e5b8858f10205a..441810a9f2270b 100644 --- a/packages/block-library/src/image/view-interactivity.js +++ b/packages/block-library/src/image/view-interactivity.js @@ -133,6 +133,16 @@ store( { } } }, + preloadLightboxImage: ( { context } ) => { + if ( ! context.core.image.preloadInitialized ) { + context.core.image.preloadInitialized = true; + const imgDom = document.createElement( 'img' ); + imgDom.setAttribute( + 'src', + context.core.image.imageUploadedSrc + ); + } + }, }, }, }, @@ -171,18 +181,6 @@ store( { } ); } }, - preloadLightboxImage: ( { context, ref } ) => { - ref.addEventListener( 'mouseover', () => { - if ( ! context.core.image.preloadInitialized ) { - context.core.image.preloadInitialized = true; - const imgDom = document.createElement( 'img' ); - imgDom.setAttribute( - 'src', - context.core.image.imageUploadedSrc - ); - } - } ); - }, initLightbox: async ( { context, ref } ) => { context.core.image.figureRef = ref.querySelector( 'figure' ); From abe29bb1e45de8f8b1266f4eb96d9ce4ee6b3a39 Mon Sep 17 00:00:00 2001 From: Kai Hao Date: Fri, 30 Jun 2023 10:46:24 +0800 Subject: [PATCH 093/266] Fix history back after entering edit mode from Patterns (#52112) --- packages/edit-site/src/components/page-library/grid-item.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/edit-site/src/components/page-library/grid-item.js b/packages/edit-site/src/components/page-library/grid-item.js index fa6793ca781786..726f7a9896adc2 100644 --- a/packages/edit-site/src/components/page-library/grid-item.js +++ b/packages/edit-site/src/components/page-library/grid-item.js @@ -52,7 +52,6 @@ export default function GridItem( { categoryId, composite, icon, item } ) { postId: item.type === USER_PATTERNS ? item.id : item.name, categoryId, categoryType: item.type, - canvas: 'view', } ); const onKeyDown = ( event ) => { From 37a91675047f1e0a2bf777d8f5fb8932b84f9b5a Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Fri, 30 Jun 2023 12:45:38 +0900 Subject: [PATCH 094/266] Command Palette: fix incorrect path and snackbar message when template part is deleted (#52034) * Command Center: Fix incorrect navigation when deleting template part * removeTemplate: consider title type --- .../src/hooks/commands/use-edit-mode-commands.js | 6 +++++- packages/edit-site/src/store/actions.js | 9 ++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js b/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js index 92ccee08592cde..e9e00b9723a125 100644 --- a/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js +++ b/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js @@ -111,6 +111,10 @@ function useManipulateDocumentCommands() { template.type === 'wp_template' ? __( 'Delete template' ) : __( 'Delete template part' ); + const path = + template.type === 'wp_template' + ? '/wp_template' + : '/wp_template_part/all'; commands.push( { name: 'core/remove-template', label, @@ -119,7 +123,7 @@ function useManipulateDocumentCommands() { removeTemplate( template ); // Navigate to the template list history.push( { - path: '/' + template.type, + path, } ); close(); }, diff --git a/packages/edit-site/src/store/actions.js b/packages/edit-site/src/store/actions.js index 8da362ca01f17d..ac07ac5beaa2af 100644 --- a/packages/edit-site/src/store/actions.js +++ b/packages/edit-site/src/store/actions.js @@ -141,11 +141,18 @@ export const removeTemplate = throw lastError; } + // Depending on how the entity was retrieved it's title might be + // an object or simple string. + const templateTitle = + typeof template.title === 'string' + ? template.title + : template.title?.rendered; + registry.dispatch( noticesStore ).createSuccessNotice( sprintf( /* translators: The template/part's name. */ __( '"%s" deleted.' ), - decodeEntities( template.title.rendered ) + decodeEntities( templateTitle ) ), { type: 'snackbar', id: 'site-editor-template-deleted-success' } ); From 4314ee4487ee27d3e9e254c646a9380dd1cb2bec Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Fri, 30 Jun 2023 13:10:24 +0900 Subject: [PATCH 095/266] DropdownMenu: fix icon style when dashicon is used (#43574) --- packages/components/CHANGELOG.md | 1 + .../components/src/dropdown-menu/style.scss | 22 ++++++++----------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 19182873e38282..411a326d4c5c7d 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -20,6 +20,7 @@ - `ConfirmDialog`: Ensure onConfirm isn't called an extra time when submitting one of the buttons using the keyboard ([#51730](https://github.com/WordPress/gutenberg/pull/51730)). - `ZStack`: ZStack: fix component bounding box to match children ([#51836](https://github.com/WordPress/gutenberg/pull/51836)). - `Modal`: Add small top padding to the content so that avoid cutting off the visible outline when hovering items ([#51829](https://github.com/WordPress/gutenberg/pull/51829)). +- `DropdownMenu`: fix icon style when dashicon is used ([#43574](https://github.com/WordPress/gutenberg/pull/43574)). ### Internal diff --git a/packages/components/src/dropdown-menu/style.scss b/packages/components/src/dropdown-menu/style.scss index 64303ea9049eb7..7d9e1b997f7804 100644 --- a/packages/components/src/dropdown-menu/style.scss +++ b/packages/components/src/dropdown-menu/style.scss @@ -30,19 +30,15 @@ height: 1px; } - &.is-active svg { - // Block UI appearance. - color: $white; - background: $gray-900; - box-shadow: 0 0 0 $border-width $gray-900; - border-radius: $border-width; - } - - // Formatting buttons - > svg { - border-radius: $radius-block-ui; - width: $button-size-small; - height: $button-size-small; + &.is-active { + svg, + .dashicon { + // Block UI appearance. + color: $white; + background: $gray-900; + box-shadow: 0 0 0 $border-width $gray-900; + border-radius: $border-width; + } } // If menu items are icon-only, make them stretch only to the icon size. From a6351f47533ea1b4a7e6250f8a8299170d3fa580 Mon Sep 17 00:00:00 2001 From: Gutenberg Repository Automation Date: Fri, 23 Jun 2023 07:58:00 +0000 Subject: [PATCH 096/266] Update changelog files --- packages/a11y/CHANGELOG.md | 2 ++ packages/a11y/package.json | 2 +- packages/annotations/CHANGELOG.md | 2 ++ packages/annotations/package.json | 2 +- packages/api-fetch/CHANGELOG.md | 2 ++ packages/api-fetch/package.json | 2 +- packages/autop/CHANGELOG.md | 2 ++ packages/autop/package.json | 2 +- packages/babel-plugin-import-jsx-pragma/CHANGELOG.md | 2 ++ packages/babel-plugin-import-jsx-pragma/package.json | 2 +- packages/babel-plugin-makepot/CHANGELOG.md | 2 ++ packages/babel-plugin-makepot/package.json | 2 +- packages/babel-preset-default/CHANGELOG.md | 2 ++ packages/babel-preset-default/package.json | 2 +- packages/base-styles/CHANGELOG.md | 2 ++ packages/base-styles/package.json | 2 +- packages/blob/CHANGELOG.md | 2 ++ packages/blob/package.json | 2 +- packages/block-directory/CHANGELOG.md | 2 ++ packages/block-directory/package.json | 2 +- packages/block-editor/CHANGELOG.md | 2 ++ packages/block-editor/package.json | 2 +- packages/block-library/CHANGELOG.md | 2 ++ packages/block-library/package.json | 2 +- packages/block-serialization-default-parser/CHANGELOG.md | 2 ++ packages/block-serialization-default-parser/package.json | 2 +- packages/block-serialization-spec-parser/CHANGELOG.md | 2 ++ packages/block-serialization-spec-parser/package.json | 2 +- packages/blocks/CHANGELOG.md | 2 ++ packages/blocks/package.json | 2 +- packages/browserslist-config/CHANGELOG.md | 2 ++ packages/browserslist-config/package.json | 2 +- packages/commands/CHANGELOG.md | 2 ++ packages/commands/package.json | 2 +- packages/components/CHANGELOG.md | 2 ++ packages/components/package.json | 2 +- packages/compose/CHANGELOG.md | 2 ++ packages/compose/package.json | 2 +- packages/core-commands/CHANGELOG.md | 2 ++ packages/core-commands/package.json | 2 +- packages/core-data/CHANGELOG.md | 2 ++ packages/core-data/package.json | 2 +- packages/create-block-tutorial-template/CHANGELOG.md | 2 ++ packages/create-block-tutorial-template/package.json | 2 +- packages/create-block/CHANGELOG.md | 2 ++ packages/create-block/package.json | 2 +- packages/customize-widgets/CHANGELOG.md | 2 ++ packages/customize-widgets/package.json | 2 +- packages/data-controls/CHANGELOG.md | 2 ++ packages/data-controls/package.json | 2 +- packages/data/CHANGELOG.md | 2 ++ packages/data/package.json | 2 +- packages/date/CHANGELOG.md | 2 ++ packages/date/package.json | 2 +- packages/dependency-extraction-webpack-plugin/CHANGELOG.md | 2 ++ packages/dependency-extraction-webpack-plugin/package.json | 2 +- packages/deprecated/CHANGELOG.md | 2 ++ packages/deprecated/package.json | 2 +- packages/docgen/CHANGELOG.md | 2 ++ packages/docgen/package.json | 2 +- packages/dom-ready/CHANGELOG.md | 2 ++ packages/dom-ready/package.json | 2 +- packages/dom/CHANGELOG.md | 2 ++ packages/dom/package.json | 2 +- packages/e2e-test-utils-playwright/CHANGELOG.md | 2 ++ packages/e2e-test-utils-playwright/package.json | 2 +- packages/e2e-test-utils/CHANGELOG.md | 2 ++ packages/e2e-test-utils/package.json | 2 +- packages/e2e-tests/CHANGELOG.md | 2 ++ packages/e2e-tests/package.json | 2 +- packages/edit-post/CHANGELOG.md | 2 ++ packages/edit-post/package.json | 2 +- packages/edit-site/CHANGELOG.md | 2 ++ packages/edit-site/package.json | 2 +- packages/edit-widgets/CHANGELOG.md | 2 ++ packages/edit-widgets/package.json | 2 +- packages/editor/CHANGELOG.md | 2 ++ packages/editor/package.json | 2 +- packages/element/CHANGELOG.md | 2 ++ packages/element/package.json | 2 +- packages/env/CHANGELOG.md | 2 ++ packages/env/package.json | 2 +- packages/escape-html/CHANGELOG.md | 2 ++ packages/escape-html/package.json | 2 +- packages/eslint-plugin/CHANGELOG.md | 2 ++ packages/eslint-plugin/package.json | 2 +- packages/format-library/CHANGELOG.md | 2 ++ packages/format-library/package.json | 2 +- packages/hooks/CHANGELOG.md | 2 ++ packages/hooks/package.json | 2 +- packages/html-entities/CHANGELOG.md | 2 ++ packages/html-entities/package.json | 2 +- packages/i18n/CHANGELOG.md | 2 ++ packages/i18n/package.json | 2 +- packages/icons/CHANGELOG.md | 2 ++ packages/icons/package.json | 2 +- packages/interface/CHANGELOG.md | 2 ++ packages/interface/package.json | 2 +- packages/is-shallow-equal/CHANGELOG.md | 2 ++ packages/is-shallow-equal/package.json | 2 +- packages/jest-console/CHANGELOG.md | 2 ++ packages/jest-console/package.json | 2 +- packages/jest-preset-default/CHANGELOG.md | 2 ++ packages/jest-preset-default/package.json | 2 +- packages/jest-puppeteer-axe/CHANGELOG.md | 2 ++ packages/jest-puppeteer-axe/package.json | 2 +- packages/keyboard-shortcuts/CHANGELOG.md | 2 ++ packages/keyboard-shortcuts/package.json | 2 +- packages/keycodes/CHANGELOG.md | 2 ++ packages/keycodes/package.json | 2 +- packages/lazy-import/CHANGELOG.md | 2 ++ packages/lazy-import/package.json | 2 +- packages/list-reusable-blocks/CHANGELOG.md | 2 ++ packages/list-reusable-blocks/package.json | 2 +- packages/media-utils/CHANGELOG.md | 2 ++ packages/media-utils/package.json | 2 +- packages/notices/CHANGELOG.md | 2 ++ packages/notices/package.json | 2 +- packages/npm-package-json-lint-config/CHANGELOG.md | 2 ++ packages/npm-package-json-lint-config/package.json | 2 +- packages/plugins/CHANGELOG.md | 2 ++ packages/plugins/package.json | 2 +- packages/postcss-plugins-preset/CHANGELOG.md | 2 ++ packages/postcss-plugins-preset/package.json | 2 +- packages/postcss-themes/CHANGELOG.md | 2 ++ packages/postcss-themes/package.json | 2 +- packages/preferences-persistence/CHANGELOG.md | 2 ++ packages/preferences-persistence/package.json | 2 +- packages/preferences/CHANGELOG.md | 2 ++ packages/preferences/package.json | 2 +- packages/prettier-config/CHANGELOG.md | 2 ++ packages/prettier-config/package.json | 2 +- packages/primitives/CHANGELOG.md | 2 ++ packages/primitives/package.json | 2 +- packages/priority-queue/CHANGELOG.md | 2 ++ packages/priority-queue/package.json | 2 +- packages/private-apis/CHANGELOG.md | 2 ++ packages/private-apis/package.json | 2 +- packages/project-management-automation/CHANGELOG.md | 2 ++ packages/project-management-automation/package.json | 2 +- packages/react-i18n/CHANGELOG.md | 2 ++ packages/react-i18n/package.json | 2 +- packages/readable-js-assets-webpack-plugin/CHANGELOG.md | 2 ++ packages/readable-js-assets-webpack-plugin/package.json | 2 +- packages/redux-routine/CHANGELOG.md | 2 ++ packages/redux-routine/package.json | 2 +- packages/reusable-blocks/CHANGELOG.md | 2 ++ packages/reusable-blocks/package.json | 2 +- packages/rich-text/CHANGELOG.md | 2 ++ packages/rich-text/package.json | 2 +- packages/router/CHANGELOG.md | 2 ++ packages/router/package.json | 2 +- packages/scripts/CHANGELOG.md | 2 ++ packages/scripts/package.json | 2 +- packages/server-side-render/CHANGELOG.md | 2 ++ packages/server-side-render/package.json | 2 +- packages/shortcode/CHANGELOG.md | 2 ++ packages/shortcode/package.json | 2 +- packages/style-engine/CHANGELOG.md | 2 ++ packages/style-engine/package.json | 2 +- packages/stylelint-config/CHANGELOG.md | 2 ++ packages/stylelint-config/package.json | 2 +- packages/token-list/CHANGELOG.md | 2 ++ packages/token-list/package.json | 2 +- packages/url/CHANGELOG.md | 2 ++ packages/url/package.json | 2 +- packages/viewport/CHANGELOG.md | 2 ++ packages/viewport/package.json | 2 +- packages/warning/CHANGELOG.md | 2 ++ packages/warning/package.json | 2 +- packages/widgets/CHANGELOG.md | 2 ++ packages/widgets/package.json | 2 +- packages/wordcount/CHANGELOG.md | 2 ++ packages/wordcount/package.json | 2 +- 174 files changed, 261 insertions(+), 87 deletions(-) diff --git a/packages/a11y/CHANGELOG.md b/packages/a11y/CHANGELOG.md index b2785a7d76aeca..98c87f9cf04440 100644 --- a/packages/a11y/CHANGELOG.md +++ b/packages/a11y/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.36.0 (2023-06-23) + ## 3.35.0 (2023-06-07) ## 3.34.0 (2023-05-24) diff --git a/packages/a11y/package.json b/packages/a11y/package.json index 29cc18c0d43b0b..3d465cf55d799f 100644 --- a/packages/a11y/package.json +++ b/packages/a11y/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/a11y", - "version": "3.35.0", + "version": "3.36.0-prerelease", "description": "Accessibility (a11y) utilities for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/annotations/CHANGELOG.md b/packages/annotations/CHANGELOG.md index fa480659cfeb9e..3f98aba3315803 100644 --- a/packages/annotations/CHANGELOG.md +++ b/packages/annotations/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 2.36.0 (2023-06-23) + ## 2.35.0 (2023-06-07) ## 2.34.0 (2023-05-24) diff --git a/packages/annotations/package.json b/packages/annotations/package.json index 72ee0c186e49f1..4b6883e1f7e23a 100644 --- a/packages/annotations/package.json +++ b/packages/annotations/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/annotations", - "version": "2.35.0", + "version": "2.36.0-prerelease", "description": "Annotate content in the Gutenberg editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/api-fetch/CHANGELOG.md b/packages/api-fetch/CHANGELOG.md index 8b5ba78728eeb8..b25f69fb4f5fcb 100644 --- a/packages/api-fetch/CHANGELOG.md +++ b/packages/api-fetch/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 6.33.0 (2023-06-23) + ## 6.32.0 (2023-06-07) ## 6.31.0 (2023-05-24) diff --git a/packages/api-fetch/package.json b/packages/api-fetch/package.json index 0e83ca03ada740..5e2b555d7efa84 100644 --- a/packages/api-fetch/package.json +++ b/packages/api-fetch/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/api-fetch", - "version": "6.32.0", + "version": "6.33.0-prerelease", "description": "Utility to make WordPress REST API requests.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/autop/CHANGELOG.md b/packages/autop/CHANGELOG.md index 49537a263d22c5..d089ca46fe2b0d 100644 --- a/packages/autop/CHANGELOG.md +++ b/packages/autop/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.36.0 (2023-06-23) + ## 3.35.0 (2023-06-07) ## 3.34.0 (2023-05-24) diff --git a/packages/autop/package.json b/packages/autop/package.json index 4614790d880cca..019ce9822a26ce 100644 --- a/packages/autop/package.json +++ b/packages/autop/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/autop", - "version": "3.35.0", + "version": "3.36.0-prerelease", "description": "WordPress's automatic paragraph functions `autop` and `removep`.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/babel-plugin-import-jsx-pragma/CHANGELOG.md b/packages/babel-plugin-import-jsx-pragma/CHANGELOG.md index 238b4bb04919ca..354c9c51d5cac0 100644 --- a/packages/babel-plugin-import-jsx-pragma/CHANGELOG.md +++ b/packages/babel-plugin-import-jsx-pragma/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.19.0 (2023-06-23) + ## 4.18.0 (2023-06-07) ## 4.17.0 (2023-05-24) diff --git a/packages/babel-plugin-import-jsx-pragma/package.json b/packages/babel-plugin-import-jsx-pragma/package.json index 9a9062e2f8e6f4..16448ef4744b6e 100644 --- a/packages/babel-plugin-import-jsx-pragma/package.json +++ b/packages/babel-plugin-import-jsx-pragma/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/babel-plugin-import-jsx-pragma", - "version": "4.18.0", + "version": "4.19.0-prerelease", "description": "Babel transform plugin for automatically injecting an import to be used as the pragma for the React JSX Transform plugin.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/babel-plugin-makepot/CHANGELOG.md b/packages/babel-plugin-makepot/CHANGELOG.md index 32004f1532e6ec..19c6d8217fd66d 100644 --- a/packages/babel-plugin-makepot/CHANGELOG.md +++ b/packages/babel-plugin-makepot/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 5.20.0 (2023-06-23) + ## 5.19.0 (2023-06-07) ## 5.18.0 (2023-05-24) diff --git a/packages/babel-plugin-makepot/package.json b/packages/babel-plugin-makepot/package.json index 10c464a0524147..dc65d396c53c1c 100644 --- a/packages/babel-plugin-makepot/package.json +++ b/packages/babel-plugin-makepot/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/babel-plugin-makepot", - "version": "5.19.0", + "version": "5.20.0-prerelease", "description": "WordPress Babel internationalization (i18n) plugin.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/babel-preset-default/CHANGELOG.md b/packages/babel-preset-default/CHANGELOG.md index 4c3b6364c01d34..4a9c7c7616d35a 100644 --- a/packages/babel-preset-default/CHANGELOG.md +++ b/packages/babel-preset-default/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 7.20.0 (2023-06-23) + ## 7.19.0 (2023-06-07) ### Enhancement diff --git a/packages/babel-preset-default/package.json b/packages/babel-preset-default/package.json index d743f24b8b4788..6ffa9ea389089c 100644 --- a/packages/babel-preset-default/package.json +++ b/packages/babel-preset-default/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/babel-preset-default", - "version": "7.19.0", + "version": "7.20.0-prerelease", "description": "Default Babel preset for WordPress development.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/base-styles/CHANGELOG.md b/packages/base-styles/CHANGELOG.md index 250bec7aa4c443..f0add1ac033f11 100644 --- a/packages/base-styles/CHANGELOG.md +++ b/packages/base-styles/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.27.0 (2023-06-23) + ## 4.26.0 (2023-06-07) ## 4.25.0 (2023-05-24) diff --git a/packages/base-styles/package.json b/packages/base-styles/package.json index 6813a74dd783a9..b1c61c02836ebc 100644 --- a/packages/base-styles/package.json +++ b/packages/base-styles/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/base-styles", - "version": "4.26.0", + "version": "4.27.0-prerelease", "description": "Base SCSS utilities and variables for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/blob/CHANGELOG.md b/packages/blob/CHANGELOG.md index edd7a8b4d7cc1e..ce0e2a0cae35d7 100644 --- a/packages/blob/CHANGELOG.md +++ b/packages/blob/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.36.0 (2023-06-23) + ## 3.35.0 (2023-06-07) ## 3.34.0 (2023-05-24) diff --git a/packages/blob/package.json b/packages/blob/package.json index e63dc67bb9c4cc..a174a84daa15af 100644 --- a/packages/blob/package.json +++ b/packages/blob/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/blob", - "version": "3.35.0", + "version": "3.36.0-prerelease", "description": "Blob utilities for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/block-directory/CHANGELOG.md b/packages/block-directory/CHANGELOG.md index f5f6f255638ee1..3614d2137dd65f 100644 --- a/packages/block-directory/CHANGELOG.md +++ b/packages/block-directory/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.13.0 (2023-06-23) + ## 4.12.0 (2023-06-07) ## 4.11.0 (2023-05-24) diff --git a/packages/block-directory/package.json b/packages/block-directory/package.json index f38e95dc2dd716..10141cd8cd28fa 100644 --- a/packages/block-directory/package.json +++ b/packages/block-directory/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-directory", - "version": "4.12.0", + "version": "4.13.0-prerelease", "description": "Extend editor with block directory features to search, download and install blocks.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/block-editor/CHANGELOG.md b/packages/block-editor/CHANGELOG.md index 3fe89d0e9df3a0..c3a92fae4bfbd7 100644 --- a/packages/block-editor/CHANGELOG.md +++ b/packages/block-editor/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 12.4.0 (2023-06-23) + ### Enhancements - Add `HeadingLevelDropdown` component for selecting H1-H6 and paragraph HTML tags from the block toolbar. diff --git a/packages/block-editor/package.json b/packages/block-editor/package.json index 64cd3f080543d1..47d46c515e66f6 100644 --- a/packages/block-editor/package.json +++ b/packages/block-editor/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-editor", - "version": "12.3.0", + "version": "12.4.0-prerelease", "description": "Generic block editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/block-library/CHANGELOG.md b/packages/block-library/CHANGELOG.md index 1711e5cd67e4d1..12d3e9ce980be5 100644 --- a/packages/block-library/CHANGELOG.md +++ b/packages/block-library/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 8.13.0 (2023-06-23) + ## 8.12.0 (2023-06-07) ## 8.11.0 (2023-05-24) diff --git a/packages/block-library/package.json b/packages/block-library/package.json index 773b173c909f54..93d9ebd9031524 100644 --- a/packages/block-library/package.json +++ b/packages/block-library/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-library", - "version": "8.12.0", + "version": "8.13.0-prerelease", "description": "Block library for the WordPress editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/block-serialization-default-parser/CHANGELOG.md b/packages/block-serialization-default-parser/CHANGELOG.md index 6f4359fd6b0105..6a1e83fff5ab39 100644 --- a/packages/block-serialization-default-parser/CHANGELOG.md +++ b/packages/block-serialization-default-parser/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.36.0 (2023-06-23) + ## 4.35.0 (2023-06-07) ## 4.34.0 (2023-05-24) diff --git a/packages/block-serialization-default-parser/package.json b/packages/block-serialization-default-parser/package.json index ea528d226b371f..a94d36c7117faa 100644 --- a/packages/block-serialization-default-parser/package.json +++ b/packages/block-serialization-default-parser/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-serialization-default-parser", - "version": "4.35.0", + "version": "4.36.0-prerelease", "description": "Block serialization specification parser for WordPress posts.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/block-serialization-spec-parser/CHANGELOG.md b/packages/block-serialization-spec-parser/CHANGELOG.md index 1e1b96b4fe7bb3..944bb874da02b3 100644 --- a/packages/block-serialization-spec-parser/CHANGELOG.md +++ b/packages/block-serialization-spec-parser/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.36.0 (2023-06-23) + ## 4.35.0 (2023-06-07) ## 4.34.0 (2023-05-24) diff --git a/packages/block-serialization-spec-parser/package.json b/packages/block-serialization-spec-parser/package.json index 7467a41ca4c202..26d66b2309bdbe 100644 --- a/packages/block-serialization-spec-parser/package.json +++ b/packages/block-serialization-spec-parser/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-serialization-spec-parser", - "version": "4.35.0", + "version": "4.36.0-prerelease", "description": "Block serialization specification parser for WordPress posts.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/blocks/CHANGELOG.md b/packages/blocks/CHANGELOG.md index d0d534a440e1c3..f26e897aebbd07 100644 --- a/packages/blocks/CHANGELOG.md +++ b/packages/blocks/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 12.13.0 (2023-06-23) + ## 12.12.0 (2023-06-07) ## 12.11.0 (2023-05-24) diff --git a/packages/blocks/package.json b/packages/blocks/package.json index de6dfadd34a74c..c684621c4f334d 100644 --- a/packages/blocks/package.json +++ b/packages/blocks/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/blocks", - "version": "12.12.0", + "version": "12.13.0-prerelease", "description": "Block API for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/browserslist-config/CHANGELOG.md b/packages/browserslist-config/CHANGELOG.md index 4116cd534504f7..cf43f3751f74ba 100644 --- a/packages/browserslist-config/CHANGELOG.md +++ b/packages/browserslist-config/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 5.19.0 (2023-06-23) + ## 5.18.0 (2023-06-07) ## 5.17.0 (2023-05-24) diff --git a/packages/browserslist-config/package.json b/packages/browserslist-config/package.json index 5019ec0c351a84..0f86d30db3238d 100644 --- a/packages/browserslist-config/package.json +++ b/packages/browserslist-config/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/browserslist-config", - "version": "5.18.0", + "version": "5.19.0-prerelease", "description": "WordPress Browserslist shared configuration.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/commands/CHANGELOG.md b/packages/commands/CHANGELOG.md index cf9b9ac9d178e5..e76f27c9d787ba 100644 --- a/packages/commands/CHANGELOG.md +++ b/packages/commands/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 0.7.0 (2023-06-23) + ## 0.6.0 (2023-06-07) ## 0.5.0 (2023-05-24) diff --git a/packages/commands/package.json b/packages/commands/package.json index 719ec304f9500f..71634f8546cd61 100644 --- a/packages/commands/package.json +++ b/packages/commands/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/commands", - "version": "0.6.0", + "version": "0.7.0-prerelease", "description": "Handles the commands menu.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 411a326d4c5c7d..d6b79db2d80521 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 25.2.0 (2023-06-23) + ### Enhancements - `SelectControl`: Added option to set hidden options. ([#51545](https://github.com/WordPress/gutenberg/pull/51545)) diff --git a/packages/components/package.json b/packages/components/package.json index 71f01450f12071..b64ca65a7bcd98 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/components", - "version": "25.1.0", + "version": "25.2.0-prerelease", "description": "UI components for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/compose/CHANGELOG.md b/packages/compose/CHANGELOG.md index 5a7919e925c2d5..dd09c0f94a2291 100644 --- a/packages/compose/CHANGELOG.md +++ b/packages/compose/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 6.13.0 (2023-06-23) + ## 6.12.0 (2023-06-07) ## 6.11.0 (2023-05-24) diff --git a/packages/compose/package.json b/packages/compose/package.json index 680686826131bc..3461449ec989a0 100644 --- a/packages/compose/package.json +++ b/packages/compose/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/compose", - "version": "6.12.0", + "version": "6.13.0-prerelease", "description": "WordPress higher-order components (HOCs).", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/core-commands/CHANGELOG.md b/packages/core-commands/CHANGELOG.md index 5df15748a5c459..7f958692b8b510 100644 --- a/packages/core-commands/CHANGELOG.md +++ b/packages/core-commands/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 0.5.0 (2023-06-23) + ## 0.4.0 (2023-06-07) ## 0.3.0 (2023-05-24) diff --git a/packages/core-commands/package.json b/packages/core-commands/package.json index ffc1e440e61864..384ef0d05f2621 100644 --- a/packages/core-commands/package.json +++ b/packages/core-commands/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/core-commands", - "version": "0.4.0", + "version": "0.5.0-prerelease", "description": "WordPress core reusable commands.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/core-data/CHANGELOG.md b/packages/core-data/CHANGELOG.md index 62500d4ee0bb4e..5b6d9857dfdc3f 100644 --- a/packages/core-data/CHANGELOG.md +++ b/packages/core-data/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 6.13.0 (2023-06-23) + ## 6.12.0 (2023-06-07) ## 6.11.0 (2023-05-24) diff --git a/packages/core-data/package.json b/packages/core-data/package.json index 05068541d23bb8..947044f946a00e 100644 --- a/packages/core-data/package.json +++ b/packages/core-data/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/core-data", - "version": "6.12.0", + "version": "6.13.0-prerelease", "description": "Access to and manipulation of core WordPress entities.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/create-block-tutorial-template/CHANGELOG.md b/packages/create-block-tutorial-template/CHANGELOG.md index a68268c069d977..57eb9ae327f32c 100644 --- a/packages/create-block-tutorial-template/CHANGELOG.md +++ b/packages/create-block-tutorial-template/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 2.24.0 (2023-06-23) + ## 2.23.0 (2023-06-07) ## 2.22.0 (2023-05-24) diff --git a/packages/create-block-tutorial-template/package.json b/packages/create-block-tutorial-template/package.json index 064b846fbd8d03..7aaede53696fc6 100644 --- a/packages/create-block-tutorial-template/package.json +++ b/packages/create-block-tutorial-template/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/create-block-tutorial-template", - "version": "2.23.0", + "version": "2.24.0-prerelease", "description": "Template for @wordpress/create-block used in the official WordPress tutorial.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/create-block/CHANGELOG.md b/packages/create-block/CHANGELOG.md index d4c0b8f17ccd68..96835f0de98a9c 100644 --- a/packages/create-block/CHANGELOG.md +++ b/packages/create-block/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.20.0 (2023-06-23) + ## 4.19.0 (2023-06-07) ## 4.18.0 (2023-05-24) diff --git a/packages/create-block/package.json b/packages/create-block/package.json index bd84b75e02adb3..350c6a9c313dfa 100644 --- a/packages/create-block/package.json +++ b/packages/create-block/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/create-block", - "version": "4.19.0", + "version": "4.20.0-prerelease", "description": "Generates PHP, JS and CSS code for registering a block for a WordPress plugin.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/customize-widgets/CHANGELOG.md b/packages/customize-widgets/CHANGELOG.md index 46f85adba1bbc3..3eec6f26451340 100644 --- a/packages/customize-widgets/CHANGELOG.md +++ b/packages/customize-widgets/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.13.0 (2023-06-23) + ## 4.12.0 (2023-06-07) ## 4.11.0 (2023-05-24) diff --git a/packages/customize-widgets/package.json b/packages/customize-widgets/package.json index 9286bbf2b662be..73ad31da8d1b87 100644 --- a/packages/customize-widgets/package.json +++ b/packages/customize-widgets/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/customize-widgets", - "version": "4.12.0", + "version": "4.13.0-prerelease", "description": "Widgets blocks in Customizer Module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/data-controls/CHANGELOG.md b/packages/data-controls/CHANGELOG.md index 469409f5ea265b..57c16160b19eb4 100644 --- a/packages/data-controls/CHANGELOG.md +++ b/packages/data-controls/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.5.0 (2023-06-23) + ## 3.4.0 (2023-06-07) ## 3.3.0 (2023-05-24) diff --git a/packages/data-controls/package.json b/packages/data-controls/package.json index f2510f0ed7fd1e..bc15cbcca7b371 100644 --- a/packages/data-controls/package.json +++ b/packages/data-controls/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/data-controls", - "version": "3.4.0", + "version": "3.5.0-prerelease", "description": "A set of common controls for the @wordpress/data api.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/data/CHANGELOG.md b/packages/data/CHANGELOG.md index a9ee8db77938ec..6b1fb31679157d 100644 --- a/packages/data/CHANGELOG.md +++ b/packages/data/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 9.6.0 (2023-06-23) + ## 9.5.0 (2023-06-07) ## 9.4.0 (2023-05-24) diff --git a/packages/data/package.json b/packages/data/package.json index af12e88f05171d..70534f32a736aa 100644 --- a/packages/data/package.json +++ b/packages/data/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/data", - "version": "9.5.0", + "version": "9.6.0-prerelease", "description": "Data module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/date/CHANGELOG.md b/packages/date/CHANGELOG.md index 82d17d1b64e04b..a20b8a26ace6c4 100644 --- a/packages/date/CHANGELOG.md +++ b/packages/date/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.36.0 (2023-06-23) + ## 4.35.0 (2023-06-07) ## 4.34.0 (2023-05-24) diff --git a/packages/date/package.json b/packages/date/package.json index 617d19e73b8142..56d310213f7fff 100644 --- a/packages/date/package.json +++ b/packages/date/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/date", - "version": "4.35.0", + "version": "4.36.0-prerelease", "description": "Date module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/dependency-extraction-webpack-plugin/CHANGELOG.md b/packages/dependency-extraction-webpack-plugin/CHANGELOG.md index f4b6501e52d07f..d67b4a45698220 100644 --- a/packages/dependency-extraction-webpack-plugin/CHANGELOG.md +++ b/packages/dependency-extraction-webpack-plugin/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.19.0 (2023-06-23) + ## 4.18.0 (2023-06-07) ## 4.17.0 (2023-05-24) diff --git a/packages/dependency-extraction-webpack-plugin/package.json b/packages/dependency-extraction-webpack-plugin/package.json index 4b8ba85aceb634..c32498d4790cfc 100644 --- a/packages/dependency-extraction-webpack-plugin/package.json +++ b/packages/dependency-extraction-webpack-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/dependency-extraction-webpack-plugin", - "version": "4.18.0", + "version": "4.19.0-prerelease", "description": "Extract WordPress script dependencies from webpack bundles.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/deprecated/CHANGELOG.md b/packages/deprecated/CHANGELOG.md index 9f536a7f5c5867..7d4bd62d699f86 100644 --- a/packages/deprecated/CHANGELOG.md +++ b/packages/deprecated/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.36.0 (2023-06-23) + ## 3.35.0 (2023-06-07) ## 3.34.0 (2023-05-24) diff --git a/packages/deprecated/package.json b/packages/deprecated/package.json index fd3fe40d39af63..939e4141b78779 100644 --- a/packages/deprecated/package.json +++ b/packages/deprecated/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/deprecated", - "version": "3.35.0", + "version": "3.36.0-prerelease", "description": "Deprecation utility for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/docgen/CHANGELOG.md b/packages/docgen/CHANGELOG.md index d0262f91ac0a27..93b3f41495b840 100644 --- a/packages/docgen/CHANGELOG.md +++ b/packages/docgen/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 1.45.0 (2023-06-23) + ## 1.44.0 (2023-06-07) ## 1.43.0 (2023-05-24) diff --git a/packages/docgen/package.json b/packages/docgen/package.json index 7a4b3cb381d4bf..46de12f012b600 100644 --- a/packages/docgen/package.json +++ b/packages/docgen/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/docgen", - "version": "1.44.0", + "version": "1.45.0-prerelease", "description": "Autogenerate public API documentation from exports and JSDoc comments.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/dom-ready/CHANGELOG.md b/packages/dom-ready/CHANGELOG.md index d984dc18a37785..d270c829083ef3 100644 --- a/packages/dom-ready/CHANGELOG.md +++ b/packages/dom-ready/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.36.0 (2023-06-23) + ## 3.35.0 (2023-06-07) ## 3.34.0 (2023-05-24) diff --git a/packages/dom-ready/package.json b/packages/dom-ready/package.json index 6fc2adf0498d6b..f12ea98ca29372 100644 --- a/packages/dom-ready/package.json +++ b/packages/dom-ready/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/dom-ready", - "version": "3.35.0", + "version": "3.36.0-prerelease", "description": "Execute callback after the DOM is loaded.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/dom/CHANGELOG.md b/packages/dom/CHANGELOG.md index 2d91ab66f02f8d..7ca2d79a9cdd87 100644 --- a/packages/dom/CHANGELOG.md +++ b/packages/dom/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.36.0 (2023-06-23) + ## 3.35.0 (2023-06-07) ## 3.34.0 (2023-05-24) diff --git a/packages/dom/package.json b/packages/dom/package.json index dc4b12618b8e21..96621a58a874b9 100644 --- a/packages/dom/package.json +++ b/packages/dom/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/dom", - "version": "3.35.0", + "version": "3.36.0-prerelease", "description": "DOM utilities module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/e2e-test-utils-playwright/CHANGELOG.md b/packages/e2e-test-utils-playwright/CHANGELOG.md index a10cf250939065..157e3e21cbf631 100644 --- a/packages/e2e-test-utils-playwright/CHANGELOG.md +++ b/packages/e2e-test-utils-playwright/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 0.4.0 (2023-06-23) + ## 0.3.0 (2023-06-07) ## 0.2.0 (2023-05-24) diff --git a/packages/e2e-test-utils-playwright/package.json b/packages/e2e-test-utils-playwright/package.json index bf5b715fc5216e..1c3f4a01bd81e6 100644 --- a/packages/e2e-test-utils-playwright/package.json +++ b/packages/e2e-test-utils-playwright/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/e2e-test-utils-playwright", - "version": "0.3.0", + "version": "0.4.0-prerelease", "description": "End-To-End (E2E) test utils for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/e2e-test-utils/CHANGELOG.md b/packages/e2e-test-utils/CHANGELOG.md index 982f0f8693b81e..643b335f883241 100644 --- a/packages/e2e-test-utils/CHANGELOG.md +++ b/packages/e2e-test-utils/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 10.7.0 (2023-06-23) + ## 10.6.0 (2023-06-07) ## 10.5.0 (2023-05-24) diff --git a/packages/e2e-test-utils/package.json b/packages/e2e-test-utils/package.json index 007f6818ddd524..1575574a140e2d 100644 --- a/packages/e2e-test-utils/package.json +++ b/packages/e2e-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/e2e-test-utils", - "version": "10.6.0", + "version": "10.7.0-prerelease", "description": "End-To-End (E2E) test utils for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/e2e-tests/CHANGELOG.md b/packages/e2e-tests/CHANGELOG.md index 5b55aad49089b3..b8a2cfdbb8f555 100644 --- a/packages/e2e-tests/CHANGELOG.md +++ b/packages/e2e-tests/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 7.7.0 (2023-06-23) + ## 7.6.0 (2023-06-07) ## 7.5.0 (2023-05-24) diff --git a/packages/e2e-tests/package.json b/packages/e2e-tests/package.json index f683ab47d82c01..5fecc056b69240 100644 --- a/packages/e2e-tests/package.json +++ b/packages/e2e-tests/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/e2e-tests", - "version": "7.6.0", + "version": "7.7.0-prerelease", "description": "End-To-End (E2E) tests for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/edit-post/CHANGELOG.md b/packages/edit-post/CHANGELOG.md index f4f6567eb0d581..bdeb9086a2dd71 100644 --- a/packages/edit-post/CHANGELOG.md +++ b/packages/edit-post/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 7.13.0 (2023-06-23) + ## 7.12.0 (2023-06-07) ## 7.11.0 (2023-05-24) diff --git a/packages/edit-post/package.json b/packages/edit-post/package.json index d9b65c2c4e3637..db25dfd46fe578 100644 --- a/packages/edit-post/package.json +++ b/packages/edit-post/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/edit-post", - "version": "7.12.0", + "version": "7.13.0-prerelease", "description": "Edit Post module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/edit-site/CHANGELOG.md b/packages/edit-site/CHANGELOG.md index 464c0d114ef8d5..55497a651f92d7 100644 --- a/packages/edit-site/CHANGELOG.md +++ b/packages/edit-site/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 5.13.0 (2023-06-23) + ### Enhancements - Site editor sidebar: add home template details and controls [#51223](https://github.com/WordPress/gutenberg/pull/51223). - Site editor sidebar: add footer to template part and ensure nested template areas display [#51669](https://github.com/WordPress/gutenberg/pull/51669). diff --git a/packages/edit-site/package.json b/packages/edit-site/package.json index ab9b8229634ba0..da9222302ae8d6 100644 --- a/packages/edit-site/package.json +++ b/packages/edit-site/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/edit-site", - "version": "5.12.0", + "version": "5.13.0-prerelease", "description": "Edit Site Page module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/edit-widgets/CHANGELOG.md b/packages/edit-widgets/CHANGELOG.md index e26cefc39f73c3..ffbaf78b4c7b7c 100644 --- a/packages/edit-widgets/CHANGELOG.md +++ b/packages/edit-widgets/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 5.13.0 (2023-06-23) + ## 5.12.0 (2023-06-07) ## 5.11.0 (2023-05-24) diff --git a/packages/edit-widgets/package.json b/packages/edit-widgets/package.json index c9a6d47feec717..fef5fca923d26a 100644 --- a/packages/edit-widgets/package.json +++ b/packages/edit-widgets/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/edit-widgets", - "version": "5.12.0", + "version": "5.13.0-prerelease", "description": "Widgets Page module for WordPress..", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/editor/CHANGELOG.md b/packages/editor/CHANGELOG.md index a4a7907f0c1ee4..25bbf0b65bfe09 100644 --- a/packages/editor/CHANGELOG.md +++ b/packages/editor/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 13.13.0 (2023-06-23) + ## 13.12.0 (2023-06-07) ## 13.11.0 (2023-05-24) diff --git a/packages/editor/package.json b/packages/editor/package.json index 7ad03e43178eac..4477d0d3a1061c 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/editor", - "version": "13.12.0", + "version": "13.13.0-prerelease", "description": "Enhanced block editor for WordPress posts.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/element/CHANGELOG.md b/packages/element/CHANGELOG.md index 02b17ca27d4b21..7512e88b669de1 100644 --- a/packages/element/CHANGELOG.md +++ b/packages/element/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 5.13.0 (2023-06-23) + ## 5.12.0 (2023-06-07) ## 5.11.0 (2023-05-24) diff --git a/packages/element/package.json b/packages/element/package.json index fd7eb976697ffe..816d358b17d735 100644 --- a/packages/element/package.json +++ b/packages/element/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/element", - "version": "5.12.0", + "version": "5.13.0-prerelease", "description": "Element React module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/env/CHANGELOG.md b/packages/env/CHANGELOG.md index 03114e7f631d8e..fe680d7b4e8464 100644 --- a/packages/env/CHANGELOG.md +++ b/packages/env/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 8.2.0 (2023-06-23) + ## 8.1.1 (2023-06-17) ### Bug fix diff --git a/packages/env/package.json b/packages/env/package.json index 2d655ec01b13d0..7c8ec2bb366ba9 100644 --- a/packages/env/package.json +++ b/packages/env/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/env", - "version": "8.1.1", + "version": "8.2.0-prerelease", "description": "A zero-config, self contained local WordPress environment for development and testing.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/escape-html/CHANGELOG.md b/packages/escape-html/CHANGELOG.md index 95d2cfe4667a32..367fe2120d21b9 100644 --- a/packages/escape-html/CHANGELOG.md +++ b/packages/escape-html/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 2.36.0 (2023-06-23) + ## 2.35.0 (2023-06-07) ## 2.34.0 (2023-05-24) diff --git a/packages/escape-html/package.json b/packages/escape-html/package.json index e9badf0c5c69cc..bcd89a63deb109 100644 --- a/packages/escape-html/package.json +++ b/packages/escape-html/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/escape-html", - "version": "2.35.0", + "version": "2.36.0-prerelease", "description": "Escape HTML utils.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md index 0c1c694cd61a7f..e5da507e40a164 100644 --- a/packages/eslint-plugin/CHANGELOG.md +++ b/packages/eslint-plugin/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 14.9.0 (2023-06-23) + ## 14.8.0 (2023-06-07) ## 14.7.0 (2023-05-24) diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 8ce44bbddb9a3f..d64f7a741ec95c 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/eslint-plugin", - "version": "14.8.0", + "version": "14.9.0-prerelease", "description": "ESLint plugin for WordPress development.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/format-library/CHANGELOG.md b/packages/format-library/CHANGELOG.md index 0f15cdf9ee8e1e..5730e4beb74589 100644 --- a/packages/format-library/CHANGELOG.md +++ b/packages/format-library/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.13.0 (2023-06-23) + ## 4.12.0 (2023-06-07) ## 4.11.0 (2023-05-24) diff --git a/packages/format-library/package.json b/packages/format-library/package.json index ed3f104c87539d..29416e033899f6 100644 --- a/packages/format-library/package.json +++ b/packages/format-library/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/format-library", - "version": "4.12.0", + "version": "4.13.0-prerelease", "description": "Format library for the WordPress editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/hooks/CHANGELOG.md b/packages/hooks/CHANGELOG.md index 2f97544a6b42d3..efbb7b009272cb 100644 --- a/packages/hooks/CHANGELOG.md +++ b/packages/hooks/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.36.0 (2023-06-23) + ## 3.35.0 (2023-06-07) ## 3.34.0 (2023-05-24) diff --git a/packages/hooks/package.json b/packages/hooks/package.json index 46834c98c0dc9f..a13f95e6ce3041 100644 --- a/packages/hooks/package.json +++ b/packages/hooks/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/hooks", - "version": "3.35.0", + "version": "3.36.0-prerelease", "description": "WordPress hooks library.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/html-entities/CHANGELOG.md b/packages/html-entities/CHANGELOG.md index 053cabfd6e8ce1..1dcbc63379fe13 100644 --- a/packages/html-entities/CHANGELOG.md +++ b/packages/html-entities/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.36.0 (2023-06-23) + ## 3.35.0 (2023-06-07) ## 3.34.0 (2023-05-24) diff --git a/packages/html-entities/package.json b/packages/html-entities/package.json index 5e45cba20ff4b3..b1df9574c40b24 100644 --- a/packages/html-entities/package.json +++ b/packages/html-entities/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/html-entities", - "version": "3.35.0", + "version": "3.36.0-prerelease", "description": "HTML entity utilities for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/i18n/CHANGELOG.md b/packages/i18n/CHANGELOG.md index 26d7a11d21c9cc..9b4b0db86019cb 100644 --- a/packages/i18n/CHANGELOG.md +++ b/packages/i18n/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.36.0 (2023-06-23) + ## 4.35.0 (2023-06-07) ## 4.34.0 (2023-05-24) diff --git a/packages/i18n/package.json b/packages/i18n/package.json index da08a785205233..3ed704ef7f0b56 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/i18n", - "version": "4.35.0", + "version": "4.36.0-prerelease", "description": "WordPress internationalization (i18n) library.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/icons/CHANGELOG.md b/packages/icons/CHANGELOG.md index 731de77b43fa87..d993d7056337d0 100644 --- a/packages/icons/CHANGELOG.md +++ b/packages/icons/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 9.27.0 (2023-06-23) + ## 9.26.0 (2023-06-07) ## 9.25.0 (2023-05-24) diff --git a/packages/icons/package.json b/packages/icons/package.json index 9c4857dd36a31e..d196e49bad084c 100644 --- a/packages/icons/package.json +++ b/packages/icons/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/icons", - "version": "9.26.0", + "version": "9.27.0-prerelease", "description": "WordPress Icons package, based on dashicon.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/interface/CHANGELOG.md b/packages/interface/CHANGELOG.md index 1d1457b209a185..e6e3947180821c 100644 --- a/packages/interface/CHANGELOG.md +++ b/packages/interface/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 5.13.0 (2023-06-23) + ## 5.12.0 (2023-06-07) ## 5.11.0 (2023-05-24) diff --git a/packages/interface/package.json b/packages/interface/package.json index 9e7c64833b0ffe..8a48da0c37df95 100644 --- a/packages/interface/package.json +++ b/packages/interface/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/interface", - "version": "5.12.0", + "version": "5.13.0-prerelease", "description": "Interface module for WordPress. The package contains shared functionality across the modern JavaScript-based WordPress screens.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/is-shallow-equal/CHANGELOG.md b/packages/is-shallow-equal/CHANGELOG.md index 939b8e2748ca34..7f8453c14ef015 100644 --- a/packages/is-shallow-equal/CHANGELOG.md +++ b/packages/is-shallow-equal/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.36.0 (2023-06-23) + ## 4.35.0 (2023-06-07) ## 4.34.0 (2023-05-24) diff --git a/packages/is-shallow-equal/package.json b/packages/is-shallow-equal/package.json index 36eb99cc2119e9..47adb7388c29ed 100644 --- a/packages/is-shallow-equal/package.json +++ b/packages/is-shallow-equal/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/is-shallow-equal", - "version": "4.35.0", + "version": "4.36.0-prerelease", "description": "Test for shallow equality between two objects or arrays.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/jest-console/CHANGELOG.md b/packages/jest-console/CHANGELOG.md index 7174bda034a77c..92711da2c80b8d 100644 --- a/packages/jest-console/CHANGELOG.md +++ b/packages/jest-console/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 7.7.0 (2023-06-23) + ## 7.6.0 (2023-06-07) ## 7.5.0 (2023-05-24) diff --git a/packages/jest-console/package.json b/packages/jest-console/package.json index 44202113e2f0a2..73776a7fa25ae3 100644 --- a/packages/jest-console/package.json +++ b/packages/jest-console/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/jest-console", - "version": "7.6.0", + "version": "7.7.0-prerelease", "description": "Custom Jest matchers for the Console object.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/jest-preset-default/CHANGELOG.md b/packages/jest-preset-default/CHANGELOG.md index 19de2ea2a0ebab..af775dd7d9aeeb 100644 --- a/packages/jest-preset-default/CHANGELOG.md +++ b/packages/jest-preset-default/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 11.7.0 (2023-06-23) + ## 11.6.0 (2023-06-07) ## 11.5.0 (2023-05-24) diff --git a/packages/jest-preset-default/package.json b/packages/jest-preset-default/package.json index 58129533c0ebcf..9eb973ecaacb53 100644 --- a/packages/jest-preset-default/package.json +++ b/packages/jest-preset-default/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/jest-preset-default", - "version": "11.6.0", + "version": "11.7.0-prerelease", "description": "Default Jest preset for WordPress development.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/jest-puppeteer-axe/CHANGELOG.md b/packages/jest-puppeteer-axe/CHANGELOG.md index 3d78cf93542667..0ad708658f82d2 100644 --- a/packages/jest-puppeteer-axe/CHANGELOG.md +++ b/packages/jest-puppeteer-axe/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 6.7.0 (2023-06-23) + ## 6.6.0 (2023-06-07) ## 6.5.0 (2023-05-24) diff --git a/packages/jest-puppeteer-axe/package.json b/packages/jest-puppeteer-axe/package.json index 4aac9c7e6f35c2..21ba900b3bfae9 100644 --- a/packages/jest-puppeteer-axe/package.json +++ b/packages/jest-puppeteer-axe/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/jest-puppeteer-axe", - "version": "6.6.0", + "version": "6.7.0-prerelease", "description": "Axe API integration with Jest and Puppeteer.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/keyboard-shortcuts/CHANGELOG.md b/packages/keyboard-shortcuts/CHANGELOG.md index 03c5593c7f4c99..ab7d37c8d98654 100644 --- a/packages/keyboard-shortcuts/CHANGELOG.md +++ b/packages/keyboard-shortcuts/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.13.0 (2023-06-23) + ## 4.12.0 (2023-06-07) ## 4.11.0 (2023-05-24) diff --git a/packages/keyboard-shortcuts/package.json b/packages/keyboard-shortcuts/package.json index ea7a1527499b3a..46b44604cb986b 100644 --- a/packages/keyboard-shortcuts/package.json +++ b/packages/keyboard-shortcuts/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/keyboard-shortcuts", - "version": "4.12.0", + "version": "4.13.0-prerelease", "description": "Handling keyboard shortcuts.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/keycodes/CHANGELOG.md b/packages/keycodes/CHANGELOG.md index 656b86c317220c..a0ed8562692a96 100644 --- a/packages/keycodes/CHANGELOG.md +++ b/packages/keycodes/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.36.0 (2023-06-23) + ## 3.35.0 (2023-06-07) ## 3.34.0 (2023-05-24) diff --git a/packages/keycodes/package.json b/packages/keycodes/package.json index df85aa2b391493..30337e4acc39f6 100644 --- a/packages/keycodes/package.json +++ b/packages/keycodes/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/keycodes", - "version": "3.35.0", + "version": "3.36.0-prerelease", "description": "Keycodes utilities for WordPress. Used to check for keyboard events across browsers/operating systems.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/lazy-import/CHANGELOG.md b/packages/lazy-import/CHANGELOG.md index 8e7726be1c274f..2b842070991a92 100644 --- a/packages/lazy-import/CHANGELOG.md +++ b/packages/lazy-import/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 1.23.0 (2023-06-23) + ## 1.22.0 (2023-06-07) ## 1.21.0 (2023-05-24) diff --git a/packages/lazy-import/package.json b/packages/lazy-import/package.json index 7cdc73bf030e6a..8e19a004f5d74f 100644 --- a/packages/lazy-import/package.json +++ b/packages/lazy-import/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/lazy-import", - "version": "1.22.0", + "version": "1.23.0-prerelease", "description": "Lazily import a module, installing it automatically if missing.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/list-reusable-blocks/CHANGELOG.md b/packages/list-reusable-blocks/CHANGELOG.md index cced1c4905e3bf..1030651eeb147b 100644 --- a/packages/list-reusable-blocks/CHANGELOG.md +++ b/packages/list-reusable-blocks/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.13.0 (2023-06-23) + ## 4.12.0 (2023-06-07) ## 4.11.0 (2023-05-24) diff --git a/packages/list-reusable-blocks/package.json b/packages/list-reusable-blocks/package.json index f460e51997bab9..0f8f86031b0075 100644 --- a/packages/list-reusable-blocks/package.json +++ b/packages/list-reusable-blocks/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/list-reusable-blocks", - "version": "4.12.0", + "version": "4.13.0-prerelease", "description": "Adding Export/Import support to the reusable blocks listing.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/media-utils/CHANGELOG.md b/packages/media-utils/CHANGELOG.md index 70a24aa038a45f..7fe2a1375a95ca 100644 --- a/packages/media-utils/CHANGELOG.md +++ b/packages/media-utils/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.27.0 (2023-06-23) + ## 4.26.0 (2023-06-07) ## 4.25.0 (2023-05-24) diff --git a/packages/media-utils/package.json b/packages/media-utils/package.json index eac759e8bdbeea..82d1d9f997285a 100644 --- a/packages/media-utils/package.json +++ b/packages/media-utils/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/media-utils", - "version": "4.26.0", + "version": "4.27.0-prerelease", "description": "WordPress Media Upload Utils.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/notices/CHANGELOG.md b/packages/notices/CHANGELOG.md index 3ae783602dbd38..49e3711961c780 100644 --- a/packages/notices/CHANGELOG.md +++ b/packages/notices/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.4.0 (2023-06-23) + ## 4.3.0 (2023-06-07) ### New Feature diff --git a/packages/notices/package.json b/packages/notices/package.json index 072208c97231d1..b25d2d07acd280 100644 --- a/packages/notices/package.json +++ b/packages/notices/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/notices", - "version": "4.3.0", + "version": "4.4.0-prerelease", "description": "State management for notices.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/npm-package-json-lint-config/CHANGELOG.md b/packages/npm-package-json-lint-config/CHANGELOG.md index 0114deb8fdbff2..f6f61cebe8e500 100644 --- a/packages/npm-package-json-lint-config/CHANGELOG.md +++ b/packages/npm-package-json-lint-config/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.21.0 (2023-06-23) + ## 4.20.0 (2023-06-07) ## 4.19.0 (2023-05-24) diff --git a/packages/npm-package-json-lint-config/package.json b/packages/npm-package-json-lint-config/package.json index 719a5f2291220f..006d276658b156 100644 --- a/packages/npm-package-json-lint-config/package.json +++ b/packages/npm-package-json-lint-config/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/npm-package-json-lint-config", - "version": "4.20.0", + "version": "4.21.0-prerelease", "description": "WordPress npm-package-json-lint shareable configuration.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/plugins/CHANGELOG.md b/packages/plugins/CHANGELOG.md index f9ae2dfb6b9231..ac77eead0af17b 100644 --- a/packages/plugins/CHANGELOG.md +++ b/packages/plugins/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 6.4.0 (2023-06-23) + ## 6.3.0 (2023-06-07) ## 6.2.0 (2023-05-24) diff --git a/packages/plugins/package.json b/packages/plugins/package.json index 89018a39499448..34252dcc1f18e7 100644 --- a/packages/plugins/package.json +++ b/packages/plugins/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/plugins", - "version": "6.3.0", + "version": "6.4.0-prerelease", "description": "Plugins module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/postcss-plugins-preset/CHANGELOG.md b/packages/postcss-plugins-preset/CHANGELOG.md index 27bdd93a3d023e..a7a7b1a4ad6628 100644 --- a/packages/postcss-plugins-preset/CHANGELOG.md +++ b/packages/postcss-plugins-preset/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.20.0 (2023-06-23) + ## 4.19.0 (2023-06-07) ## 4.18.0 (2023-05-24) diff --git a/packages/postcss-plugins-preset/package.json b/packages/postcss-plugins-preset/package.json index 18813e314522dc..39eb9c2cff32c3 100644 --- a/packages/postcss-plugins-preset/package.json +++ b/packages/postcss-plugins-preset/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/postcss-plugins-preset", - "version": "4.19.0", + "version": "4.20.0-prerelease", "description": "PostCSS sharable plugins preset for WordPress development.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/postcss-themes/CHANGELOG.md b/packages/postcss-themes/CHANGELOG.md index 1f8438cf9e52f8..8736e223d7747a 100644 --- a/packages/postcss-themes/CHANGELOG.md +++ b/packages/postcss-themes/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 5.19.0 (2023-06-23) + ## 5.18.0 (2023-06-07) ## 5.17.0 (2023-05-24) diff --git a/packages/postcss-themes/package.json b/packages/postcss-themes/package.json index 04430c8f10cb1e..4eabcdfcd43219 100644 --- a/packages/postcss-themes/package.json +++ b/packages/postcss-themes/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/postcss-themes", - "version": "5.18.0", + "version": "5.19.0-prerelease", "description": "PostCSS plugin to generate theme colors.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/preferences-persistence/CHANGELOG.md b/packages/preferences-persistence/CHANGELOG.md index a73022e8f176cf..f9b58be91ed521 100644 --- a/packages/preferences-persistence/CHANGELOG.md +++ b/packages/preferences-persistence/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 1.28.0 (2023-06-23) + ## 1.27.0 (2023-06-07) ## 1.26.0 (2023-05-24) diff --git a/packages/preferences-persistence/package.json b/packages/preferences-persistence/package.json index 576749be283112..85d8425c3d5d0e 100644 --- a/packages/preferences-persistence/package.json +++ b/packages/preferences-persistence/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/preferences-persistence", - "version": "1.27.0", + "version": "1.28.0-prerelease", "description": "Persistence utilities for `wordpress/preferences`.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/preferences/CHANGELOG.md b/packages/preferences/CHANGELOG.md index 0165a8b81b271e..f2576860ef7059 100644 --- a/packages/preferences/CHANGELOG.md +++ b/packages/preferences/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.13.0 (2023-06-23) + ## 3.12.0 (2023-06-07) ## 3.11.0 (2023-05-24) diff --git a/packages/preferences/package.json b/packages/preferences/package.json index 0896545f2bacb3..d014c36ef00ed4 100644 --- a/packages/preferences/package.json +++ b/packages/preferences/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/preferences", - "version": "3.12.0", + "version": "3.13.0-prerelease", "description": "Utilities for managing WordPress preferences.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/prettier-config/CHANGELOG.md b/packages/prettier-config/CHANGELOG.md index 83045feafd9b1f..f3174145a37de5 100644 --- a/packages/prettier-config/CHANGELOG.md +++ b/packages/prettier-config/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 2.19.0 (2023-06-23) + ## 2.18.0 (2023-06-07) ## 2.17.0 (2023-05-24) diff --git a/packages/prettier-config/package.json b/packages/prettier-config/package.json index 9eba4985138e22..a503ac304e4788 100644 --- a/packages/prettier-config/package.json +++ b/packages/prettier-config/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/prettier-config", - "version": "2.18.0", + "version": "2.19.0-prerelease", "description": "WordPress Prettier shared configuration.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/primitives/CHANGELOG.md b/packages/primitives/CHANGELOG.md index 8fa404d523f853..64c1773f098900 100644 --- a/packages/primitives/CHANGELOG.md +++ b/packages/primitives/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.34.0 (2023-06-23) + ## 3.33.0 (2023-06-07) ## 3.32.0 (2023-05-24) diff --git a/packages/primitives/package.json b/packages/primitives/package.json index 03d3391c4cef47..95ba60284d8ba8 100644 --- a/packages/primitives/package.json +++ b/packages/primitives/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/primitives", - "version": "3.33.0", + "version": "3.34.0-prerelease", "description": "WordPress cross-platform primitives.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/priority-queue/CHANGELOG.md b/packages/priority-queue/CHANGELOG.md index 88f6c451a759fa..4376eae72d788c 100644 --- a/packages/priority-queue/CHANGELOG.md +++ b/packages/priority-queue/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 2.36.0 (2023-06-23) + ## 2.35.0 (2023-06-07) ## 2.34.0 (2023-05-24) diff --git a/packages/priority-queue/package.json b/packages/priority-queue/package.json index b1cf6affdd1794..a843a7c1d32610 100644 --- a/packages/priority-queue/package.json +++ b/packages/priority-queue/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/priority-queue", - "version": "2.35.0", + "version": "2.36.0-prerelease", "description": "Generic browser priority queue.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/private-apis/CHANGELOG.md b/packages/private-apis/CHANGELOG.md index 115daa64ff2342..831800ac017c23 100644 --- a/packages/private-apis/CHANGELOG.md +++ b/packages/private-apis/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 0.18.0 (2023-06-23) + ## 0.17.0 (2023-06-07) ## 0.16.0 (2023-05-24) diff --git a/packages/private-apis/package.json b/packages/private-apis/package.json index 98cac364bc1885..8d94f677fa340e 100644 --- a/packages/private-apis/package.json +++ b/packages/private-apis/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/private-apis", - "version": "0.17.0", + "version": "0.18.0-prerelease", "description": "Internal experimental APIs for WordPress core.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/project-management-automation/CHANGELOG.md b/packages/project-management-automation/CHANGELOG.md index 7b3ec58ae375ae..4c9fe0e631a6ca 100644 --- a/packages/project-management-automation/CHANGELOG.md +++ b/packages/project-management-automation/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 1.35.0 (2023-06-23) + ## 1.34.0 (2023-06-07) ## 1.33.0 (2023-05-24) diff --git a/packages/project-management-automation/package.json b/packages/project-management-automation/package.json index 90daef804a9b35..30ca2998405ea0 100644 --- a/packages/project-management-automation/package.json +++ b/packages/project-management-automation/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/project-management-automation", - "version": "1.34.0", + "version": "1.35.0-prerelease", "description": "GitHub Action that implements various automation to assist with managing the Gutenberg GitHub repository.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/react-i18n/CHANGELOG.md b/packages/react-i18n/CHANGELOG.md index 615b3686630ce4..bfe0797d50a456 100644 --- a/packages/react-i18n/CHANGELOG.md +++ b/packages/react-i18n/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.34.0 (2023-06-23) + ## 3.33.0 (2023-06-07) ## 3.32.0 (2023-05-24) diff --git a/packages/react-i18n/package.json b/packages/react-i18n/package.json index 5a0af535cf5532..56fe20d7f6fd78 100644 --- a/packages/react-i18n/package.json +++ b/packages/react-i18n/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/react-i18n", - "version": "3.33.0", + "version": "3.34.0-prerelease", "description": "React bindings for @wordpress/i18n.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/readable-js-assets-webpack-plugin/CHANGELOG.md b/packages/readable-js-assets-webpack-plugin/CHANGELOG.md index 0c5e3219d0e57c..a4b5ab64514ed4 100644 --- a/packages/readable-js-assets-webpack-plugin/CHANGELOG.md +++ b/packages/readable-js-assets-webpack-plugin/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 2.19.0 (2023-06-23) + ## 2.18.0 (2023-06-07) ## 2.17.0 (2023-05-24) diff --git a/packages/readable-js-assets-webpack-plugin/package.json b/packages/readable-js-assets-webpack-plugin/package.json index 0192ff4613581c..2eab266c74fb2b 100644 --- a/packages/readable-js-assets-webpack-plugin/package.json +++ b/packages/readable-js-assets-webpack-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/readable-js-assets-webpack-plugin", - "version": "2.18.0", + "version": "2.19.0-prerelease", "description": "Generate a readable JS file for each JS asset.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/redux-routine/CHANGELOG.md b/packages/redux-routine/CHANGELOG.md index 5e308eaa11d242..33b6483b169384 100644 --- a/packages/redux-routine/CHANGELOG.md +++ b/packages/redux-routine/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.36.0 (2023-06-23) + ## 4.35.0 (2023-06-07) ## 4.34.0 (2023-05-24) diff --git a/packages/redux-routine/package.json b/packages/redux-routine/package.json index c619a5d078a03a..2a3fed69a3cc98 100644 --- a/packages/redux-routine/package.json +++ b/packages/redux-routine/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/redux-routine", - "version": "4.35.0", + "version": "4.36.0-prerelease", "description": "Redux middleware for generator coroutines.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/reusable-blocks/CHANGELOG.md b/packages/reusable-blocks/CHANGELOG.md index e0e9118f87fa89..997f02978feb9f 100644 --- a/packages/reusable-blocks/CHANGELOG.md +++ b/packages/reusable-blocks/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.13.0 (2023-06-23) + ## 4.12.0 (2023-06-07) ## 4.11.0 (2023-05-24) diff --git a/packages/reusable-blocks/package.json b/packages/reusable-blocks/package.json index 8140ded854efb4..b92cb87360b71c 100644 --- a/packages/reusable-blocks/package.json +++ b/packages/reusable-blocks/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/reusable-blocks", - "version": "4.12.0", + "version": "4.13.0-prerelease", "description": "Reusable blocks utilities.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/rich-text/CHANGELOG.md b/packages/rich-text/CHANGELOG.md index b62c656ee6fdc0..0b5dde73c7cc47 100644 --- a/packages/rich-text/CHANGELOG.md +++ b/packages/rich-text/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 6.13.0 (2023-06-23) + ## 6.12.0 (2023-06-07) ## 6.11.0 (2023-05-24) diff --git a/packages/rich-text/package.json b/packages/rich-text/package.json index ba7cff313ea7c0..c1d73863f40f28 100644 --- a/packages/rich-text/package.json +++ b/packages/rich-text/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/rich-text", - "version": "6.12.0", + "version": "6.13.0-prerelease", "description": "Rich text value and manipulation API.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/router/CHANGELOG.md b/packages/router/CHANGELOG.md index 08f5bcbb5ec166..08ad1a4e400dd2 100644 --- a/packages/router/CHANGELOG.md +++ b/packages/router/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 0.5.0 (2023-06-23) + ## 0.4.0 (2023-06-07) ## 0.3.0 (2023-05-24) diff --git a/packages/router/package.json b/packages/router/package.json index 643542fe04d854..45ae63c2c8f49a 100644 --- a/packages/router/package.json +++ b/packages/router/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/router", - "version": "0.4.0", + "version": "0.5.0-prerelease", "description": "Router API for WordPress pages.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/scripts/CHANGELOG.md b/packages/scripts/CHANGELOG.md index d77fc6f49eb533..38a0c3e956526d 100644 --- a/packages/scripts/CHANGELOG.md +++ b/packages/scripts/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 26.7.0 (2023-06-23) + ## 26.6.0 (2023-06-07) ### Enhancements diff --git a/packages/scripts/package.json b/packages/scripts/package.json index e803d69634d291..2728ca5165f3f9 100644 --- a/packages/scripts/package.json +++ b/packages/scripts/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/scripts", - "version": "26.6.0", + "version": "26.7.0-prerelease", "description": "Collection of reusable scripts for WordPress development.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/server-side-render/CHANGELOG.md b/packages/server-side-render/CHANGELOG.md index bc10cae4a0711c..4fd70fa8ce29a7 100644 --- a/packages/server-side-render/CHANGELOG.md +++ b/packages/server-side-render/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.13.0 (2023-06-23) + ## 4.12.0 (2023-06-07) ## 4.11.0 (2023-05-24) diff --git a/packages/server-side-render/package.json b/packages/server-side-render/package.json index 8e6754ab06fb37..5bc40a4fa9a7d4 100644 --- a/packages/server-side-render/package.json +++ b/packages/server-side-render/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/server-side-render", - "version": "4.12.0", + "version": "4.13.0-prerelease", "description": "The component used with WordPress to server-side render a preview of dynamic blocks to display in the editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/shortcode/CHANGELOG.md b/packages/shortcode/CHANGELOG.md index 29d990b8f8b600..7eff85ced8717d 100644 --- a/packages/shortcode/CHANGELOG.md +++ b/packages/shortcode/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.36.0 (2023-06-23) + ## 3.35.0 (2023-06-07) ## 3.34.0 (2023-05-24) diff --git a/packages/shortcode/package.json b/packages/shortcode/package.json index edaee35591b5c3..300bb259ca7c22 100644 --- a/packages/shortcode/package.json +++ b/packages/shortcode/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/shortcode", - "version": "3.35.0", + "version": "3.36.0-prerelease", "description": "Shortcode module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/style-engine/CHANGELOG.md b/packages/style-engine/CHANGELOG.md index 7b4c5d95a9d042..47f047cf8f8ad0 100644 --- a/packages/style-engine/CHANGELOG.md +++ b/packages/style-engine/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 1.19.0 (2023-06-23) + ## 1.18.0 (2023-06-07) ## 1.17.0 (2023-05-24) diff --git a/packages/style-engine/package.json b/packages/style-engine/package.json index ef86fbb6f18ec5..dd1a275c974246 100644 --- a/packages/style-engine/package.json +++ b/packages/style-engine/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/style-engine", - "version": "1.18.0", + "version": "1.19.0-prerelease", "description": "A suite of parsers and compilers for WordPress styles.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/stylelint-config/CHANGELOG.md b/packages/stylelint-config/CHANGELOG.md index a2150acf8a6370..291de61fb2920c 100644 --- a/packages/stylelint-config/CHANGELOG.md +++ b/packages/stylelint-config/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 21.19.0 (2023-06-23) + ## 21.18.0 (2023-06-07) ## 21.17.0 (2023-05-24) diff --git a/packages/stylelint-config/package.json b/packages/stylelint-config/package.json index 9d03be8d8c453c..03dab1655221d6 100644 --- a/packages/stylelint-config/package.json +++ b/packages/stylelint-config/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/stylelint-config", - "version": "21.18.0", + "version": "21.19.0-prerelease", "description": "stylelint config for WordPress development.", "author": "The WordPress Contributors", "license": "MIT", diff --git a/packages/token-list/CHANGELOG.md b/packages/token-list/CHANGELOG.md index b76037550a8603..fc0e58fad832d7 100644 --- a/packages/token-list/CHANGELOG.md +++ b/packages/token-list/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 2.36.0 (2023-06-23) + ## 2.35.0 (2023-06-07) ## 2.34.0 (2023-05-24) diff --git a/packages/token-list/package.json b/packages/token-list/package.json index 059406cacac758..ce67241a6a34fc 100644 --- a/packages/token-list/package.json +++ b/packages/token-list/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/token-list", - "version": "2.35.0", + "version": "2.36.0-prerelease", "description": "Constructable, plain JavaScript DOMTokenList implementation, supporting non-browser runtimes.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/url/CHANGELOG.md b/packages/url/CHANGELOG.md index 868517058ef849..6f2dbc3a896cd7 100644 --- a/packages/url/CHANGELOG.md +++ b/packages/url/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.37.0 (2023-06-23) + ## 3.36.0 (2023-06-07) ## 3.35.0 (2023-05-24) diff --git a/packages/url/package.json b/packages/url/package.json index c49176bb74817e..a416310f2f8d16 100644 --- a/packages/url/package.json +++ b/packages/url/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/url", - "version": "3.36.0", + "version": "3.37.0-prerelease", "description": "WordPress URL utilities.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/viewport/CHANGELOG.md b/packages/viewport/CHANGELOG.md index f0e72ee4a42733..d179001a47927d 100644 --- a/packages/viewport/CHANGELOG.md +++ b/packages/viewport/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 5.13.0 (2023-06-23) + ## 5.12.0 (2023-06-07) ## 5.11.0 (2023-05-24) diff --git a/packages/viewport/package.json b/packages/viewport/package.json index e5a25642022399..5a853bc2670098 100644 --- a/packages/viewport/package.json +++ b/packages/viewport/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/viewport", - "version": "5.12.0", + "version": "5.13.0-prerelease", "description": "Viewport module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/warning/CHANGELOG.md b/packages/warning/CHANGELOG.md index 738cfc7016c65a..f6588ec7fd5e4c 100644 --- a/packages/warning/CHANGELOG.md +++ b/packages/warning/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 2.36.0 (2023-06-23) + ## 2.35.0 (2023-06-07) ## 2.34.0 (2023-05-24) diff --git a/packages/warning/package.json b/packages/warning/package.json index 21c0a69de8ad49..0ef3ad663b65bd 100644 --- a/packages/warning/package.json +++ b/packages/warning/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/warning", - "version": "2.35.0", + "version": "2.36.0-prerelease", "description": "Warning utility for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/widgets/CHANGELOG.md b/packages/widgets/CHANGELOG.md index d4573323e3412e..5bb9d11460ebf8 100644 --- a/packages/widgets/CHANGELOG.md +++ b/packages/widgets/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.13.0 (2023-06-23) + ## 3.12.0 (2023-06-07) ## 3.11.0 (2023-05-24) diff --git a/packages/widgets/package.json b/packages/widgets/package.json index 3d2c77b0e5ce21..102ec5d75fc22d 100644 --- a/packages/widgets/package.json +++ b/packages/widgets/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/widgets", - "version": "3.12.0", + "version": "3.13.0-prerelease", "description": "Functionality used by the widgets block editor in the Widgets screen and the Customizer.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/wordcount/CHANGELOG.md b/packages/wordcount/CHANGELOG.md index 649fafa44f3811..7fe0bd05ebfd45 100644 --- a/packages/wordcount/CHANGELOG.md +++ b/packages/wordcount/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.36.0 (2023-06-23) + ## 3.35.0 (2023-06-07) ## 3.34.0 (2023-05-24) diff --git a/packages/wordcount/package.json b/packages/wordcount/package.json index d1ad8c0ff4666b..2e8071ec295f89 100644 --- a/packages/wordcount/package.json +++ b/packages/wordcount/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/wordcount", - "version": "3.35.0", + "version": "3.36.0-prerelease", "description": "WordPress word count utility.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", From e00ec438b9a1833008eb4e754fb2ad18c74ffeea Mon Sep 17 00:00:00 2001 From: Gutenberg Repository Automation Date: Fri, 23 Jun 2023 08:00:13 +0000 Subject: [PATCH 097/266] chore(release): publish - @wordpress/a11y@3.36.0 - @wordpress/annotations@2.36.0 - @wordpress/api-fetch@6.33.0 - @wordpress/autop@3.36.0 - @wordpress/babel-plugin-import-jsx-pragma@4.19.0 - @wordpress/babel-plugin-makepot@5.20.0 - @wordpress/babel-preset-default@7.20.0 - @wordpress/base-styles@4.27.0 - @wordpress/blob@3.36.0 - @wordpress/block-directory@4.13.0 - @wordpress/block-editor@12.4.0 - @wordpress/block-library@8.13.0 - @wordpress/block-serialization-default-parser@4.36.0 - @wordpress/block-serialization-spec-parser@4.36.0 - @wordpress/blocks@12.13.0 - @wordpress/browserslist-config@5.19.0 - @wordpress/commands@0.7.0 - @wordpress/components@25.2.0 - @wordpress/compose@6.13.0 - @wordpress/core-commands@0.5.0 - @wordpress/core-data@6.13.0 - @wordpress/create-block-tutorial-template@2.24.0 - @wordpress/create-block@4.20.0 - @wordpress/customize-widgets@4.13.0 - @wordpress/data-controls@3.5.0 - @wordpress/data@9.6.0 - @wordpress/date@4.36.0 - @wordpress/dependency-extraction-webpack-plugin@4.19.0 - @wordpress/deprecated@3.36.0 - @wordpress/docgen@1.45.0 - @wordpress/dom-ready@3.36.0 - @wordpress/dom@3.36.0 - @wordpress/e2e-test-utils-playwright@0.4.0 - @wordpress/e2e-test-utils@10.7.0 - @wordpress/e2e-tests@7.7.0 - @wordpress/edit-post@7.13.0 - @wordpress/edit-site@5.13.0 - @wordpress/edit-widgets@5.13.0 - @wordpress/editor@13.13.0 - @wordpress/element@5.13.0 - @wordpress/env@8.2.0 - @wordpress/escape-html@2.36.0 - @wordpress/eslint-plugin@14.9.0 - @wordpress/format-library@4.13.0 - @wordpress/hooks@3.36.0 - @wordpress/html-entities@3.36.0 - @wordpress/i18n@4.36.0 - @wordpress/icons@9.27.0 - @wordpress/interface@5.13.0 - @wordpress/is-shallow-equal@4.36.0 - @wordpress/jest-console@7.7.0 - @wordpress/jest-preset-default@11.7.0 - @wordpress/jest-puppeteer-axe@6.7.0 - @wordpress/keyboard-shortcuts@4.13.0 - @wordpress/keycodes@3.36.0 - @wordpress/lazy-import@1.23.0 - @wordpress/list-reusable-blocks@4.13.0 - @wordpress/media-utils@4.27.0 - @wordpress/notices@4.4.0 - @wordpress/npm-package-json-lint-config@4.21.0 - @wordpress/plugins@6.4.0 - @wordpress/postcss-plugins-preset@4.20.0 - @wordpress/postcss-themes@5.19.0 - @wordpress/preferences-persistence@1.28.0 - @wordpress/preferences@3.13.0 - @wordpress/prettier-config@2.19.0 - @wordpress/primitives@3.34.0 - @wordpress/priority-queue@2.36.0 - @wordpress/private-apis@0.18.0 - @wordpress/project-management-automation@1.35.0 - @wordpress/react-i18n@3.34.0 - @wordpress/readable-js-assets-webpack-plugin@2.19.0 - @wordpress/redux-routine@4.36.0 - @wordpress/reusable-blocks@4.13.0 - @wordpress/rich-text@6.13.0 - @wordpress/router@0.5.0 - @wordpress/scripts@26.7.0 - @wordpress/server-side-render@4.13.0 - @wordpress/shortcode@3.36.0 - @wordpress/style-engine@1.19.0 - @wordpress/stylelint-config@21.19.0 - @wordpress/token-list@2.36.0 - @wordpress/url@3.37.0 - @wordpress/viewport@5.13.0 - @wordpress/warning@2.36.0 - @wordpress/widgets@3.13.0 - @wordpress/wordcount@3.36.0 --- packages/a11y/package.json | 2 +- packages/annotations/package.json | 2 +- packages/api-fetch/package.json | 2 +- packages/autop/package.json | 2 +- packages/babel-plugin-import-jsx-pragma/package.json | 2 +- packages/babel-plugin-makepot/package.json | 2 +- packages/babel-preset-default/package.json | 2 +- packages/base-styles/package.json | 2 +- packages/blob/package.json | 2 +- packages/block-directory/package.json | 2 +- packages/block-editor/package.json | 2 +- packages/block-library/package.json | 2 +- packages/block-serialization-default-parser/package.json | 2 +- packages/block-serialization-spec-parser/package.json | 2 +- packages/blocks/package.json | 2 +- packages/browserslist-config/package.json | 2 +- packages/commands/package.json | 2 +- packages/components/package.json | 2 +- packages/compose/package.json | 2 +- packages/core-commands/package.json | 2 +- packages/core-data/package.json | 2 +- packages/create-block-tutorial-template/package.json | 2 +- packages/create-block/package.json | 2 +- packages/customize-widgets/package.json | 2 +- packages/data-controls/package.json | 2 +- packages/data/package.json | 2 +- packages/date/package.json | 2 +- packages/dependency-extraction-webpack-plugin/package.json | 2 +- packages/deprecated/package.json | 2 +- packages/docgen/package.json | 2 +- packages/dom-ready/package.json | 2 +- packages/dom/package.json | 2 +- packages/e2e-test-utils-playwright/package.json | 2 +- packages/e2e-test-utils/package.json | 2 +- packages/e2e-tests/package.json | 2 +- packages/edit-post/package.json | 2 +- packages/edit-site/package.json | 2 +- packages/edit-widgets/package.json | 2 +- packages/editor/package.json | 2 +- packages/element/package.json | 2 +- packages/env/package.json | 2 +- packages/escape-html/package.json | 2 +- packages/eslint-plugin/package.json | 2 +- packages/format-library/package.json | 2 +- packages/hooks/package.json | 2 +- packages/html-entities/package.json | 2 +- packages/i18n/package.json | 2 +- packages/icons/package.json | 2 +- packages/interface/package.json | 2 +- packages/is-shallow-equal/package.json | 2 +- packages/jest-console/package.json | 2 +- packages/jest-preset-default/package.json | 2 +- packages/jest-puppeteer-axe/package.json | 2 +- packages/keyboard-shortcuts/package.json | 2 +- packages/keycodes/package.json | 2 +- packages/lazy-import/package.json | 2 +- packages/list-reusable-blocks/package.json | 2 +- packages/media-utils/package.json | 2 +- packages/notices/package.json | 2 +- packages/npm-package-json-lint-config/package.json | 2 +- packages/plugins/package.json | 2 +- packages/postcss-plugins-preset/package.json | 2 +- packages/postcss-themes/package.json | 2 +- packages/preferences-persistence/package.json | 2 +- packages/preferences/package.json | 2 +- packages/prettier-config/package.json | 2 +- packages/primitives/package.json | 2 +- packages/priority-queue/package.json | 2 +- packages/private-apis/package.json | 2 +- packages/project-management-automation/package.json | 2 +- packages/react-i18n/package.json | 2 +- packages/readable-js-assets-webpack-plugin/package.json | 2 +- packages/redux-routine/package.json | 2 +- packages/reusable-blocks/package.json | 2 +- packages/rich-text/package.json | 2 +- packages/router/package.json | 2 +- packages/scripts/package.json | 2 +- packages/server-side-render/package.json | 2 +- packages/shortcode/package.json | 2 +- packages/style-engine/package.json | 2 +- packages/stylelint-config/package.json | 2 +- packages/token-list/package.json | 2 +- packages/url/package.json | 2 +- packages/viewport/package.json | 2 +- packages/warning/package.json | 2 +- packages/widgets/package.json | 2 +- packages/wordcount/package.json | 2 +- 87 files changed, 87 insertions(+), 87 deletions(-) diff --git a/packages/a11y/package.json b/packages/a11y/package.json index 3d465cf55d799f..d6f0690a5bb009 100644 --- a/packages/a11y/package.json +++ b/packages/a11y/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/a11y", - "version": "3.36.0-prerelease", + "version": "3.36.0", "description": "Accessibility (a11y) utilities for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/annotations/package.json b/packages/annotations/package.json index 4b6883e1f7e23a..b8f586b187725a 100644 --- a/packages/annotations/package.json +++ b/packages/annotations/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/annotations", - "version": "2.36.0-prerelease", + "version": "2.36.0", "description": "Annotate content in the Gutenberg editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/api-fetch/package.json b/packages/api-fetch/package.json index 5e2b555d7efa84..68ee06ae8bc799 100644 --- a/packages/api-fetch/package.json +++ b/packages/api-fetch/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/api-fetch", - "version": "6.33.0-prerelease", + "version": "6.33.0", "description": "Utility to make WordPress REST API requests.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/autop/package.json b/packages/autop/package.json index 019ce9822a26ce..f7c5fa2d5a3a2a 100644 --- a/packages/autop/package.json +++ b/packages/autop/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/autop", - "version": "3.36.0-prerelease", + "version": "3.36.0", "description": "WordPress's automatic paragraph functions `autop` and `removep`.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/babel-plugin-import-jsx-pragma/package.json b/packages/babel-plugin-import-jsx-pragma/package.json index 16448ef4744b6e..f4c87b4ad0c834 100644 --- a/packages/babel-plugin-import-jsx-pragma/package.json +++ b/packages/babel-plugin-import-jsx-pragma/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/babel-plugin-import-jsx-pragma", - "version": "4.19.0-prerelease", + "version": "4.19.0", "description": "Babel transform plugin for automatically injecting an import to be used as the pragma for the React JSX Transform plugin.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/babel-plugin-makepot/package.json b/packages/babel-plugin-makepot/package.json index dc65d396c53c1c..8009a3797bcb29 100644 --- a/packages/babel-plugin-makepot/package.json +++ b/packages/babel-plugin-makepot/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/babel-plugin-makepot", - "version": "5.20.0-prerelease", + "version": "5.20.0", "description": "WordPress Babel internationalization (i18n) plugin.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/babel-preset-default/package.json b/packages/babel-preset-default/package.json index 6ffa9ea389089c..a519772e853adb 100644 --- a/packages/babel-preset-default/package.json +++ b/packages/babel-preset-default/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/babel-preset-default", - "version": "7.20.0-prerelease", + "version": "7.20.0", "description": "Default Babel preset for WordPress development.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/base-styles/package.json b/packages/base-styles/package.json index b1c61c02836ebc..e0b1fd5624fd91 100644 --- a/packages/base-styles/package.json +++ b/packages/base-styles/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/base-styles", - "version": "4.27.0-prerelease", + "version": "4.27.0", "description": "Base SCSS utilities and variables for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/blob/package.json b/packages/blob/package.json index a174a84daa15af..56d1b67f37aab7 100644 --- a/packages/blob/package.json +++ b/packages/blob/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/blob", - "version": "3.36.0-prerelease", + "version": "3.36.0", "description": "Blob utilities for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/block-directory/package.json b/packages/block-directory/package.json index 10141cd8cd28fa..54ddb762598018 100644 --- a/packages/block-directory/package.json +++ b/packages/block-directory/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-directory", - "version": "4.13.0-prerelease", + "version": "4.13.0", "description": "Extend editor with block directory features to search, download and install blocks.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/block-editor/package.json b/packages/block-editor/package.json index 47d46c515e66f6..634419c87954af 100644 --- a/packages/block-editor/package.json +++ b/packages/block-editor/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-editor", - "version": "12.4.0-prerelease", + "version": "12.4.0", "description": "Generic block editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/block-library/package.json b/packages/block-library/package.json index 93d9ebd9031524..213d6690f7afe8 100644 --- a/packages/block-library/package.json +++ b/packages/block-library/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-library", - "version": "8.13.0-prerelease", + "version": "8.13.0", "description": "Block library for the WordPress editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/block-serialization-default-parser/package.json b/packages/block-serialization-default-parser/package.json index a94d36c7117faa..b170b16f02770b 100644 --- a/packages/block-serialization-default-parser/package.json +++ b/packages/block-serialization-default-parser/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-serialization-default-parser", - "version": "4.36.0-prerelease", + "version": "4.36.0", "description": "Block serialization specification parser for WordPress posts.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/block-serialization-spec-parser/package.json b/packages/block-serialization-spec-parser/package.json index 26d66b2309bdbe..8c8653198d3484 100644 --- a/packages/block-serialization-spec-parser/package.json +++ b/packages/block-serialization-spec-parser/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-serialization-spec-parser", - "version": "4.36.0-prerelease", + "version": "4.36.0", "description": "Block serialization specification parser for WordPress posts.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/blocks/package.json b/packages/blocks/package.json index c684621c4f334d..582774a9d6a1c0 100644 --- a/packages/blocks/package.json +++ b/packages/blocks/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/blocks", - "version": "12.13.0-prerelease", + "version": "12.13.0", "description": "Block API for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/browserslist-config/package.json b/packages/browserslist-config/package.json index 0f86d30db3238d..d90ecf77f0ce0a 100644 --- a/packages/browserslist-config/package.json +++ b/packages/browserslist-config/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/browserslist-config", - "version": "5.19.0-prerelease", + "version": "5.19.0", "description": "WordPress Browserslist shared configuration.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/commands/package.json b/packages/commands/package.json index 71634f8546cd61..92d7cd61bd16c4 100644 --- a/packages/commands/package.json +++ b/packages/commands/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/commands", - "version": "0.7.0-prerelease", + "version": "0.7.0", "description": "Handles the commands menu.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/components/package.json b/packages/components/package.json index b64ca65a7bcd98..3b2ffe6355e4f5 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/components", - "version": "25.2.0-prerelease", + "version": "25.2.0", "description": "UI components for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/compose/package.json b/packages/compose/package.json index 3461449ec989a0..6fc4d8c9db746b 100644 --- a/packages/compose/package.json +++ b/packages/compose/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/compose", - "version": "6.13.0-prerelease", + "version": "6.13.0", "description": "WordPress higher-order components (HOCs).", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/core-commands/package.json b/packages/core-commands/package.json index 384ef0d05f2621..ed06ff22c3fd89 100644 --- a/packages/core-commands/package.json +++ b/packages/core-commands/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/core-commands", - "version": "0.5.0-prerelease", + "version": "0.5.0", "description": "WordPress core reusable commands.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/core-data/package.json b/packages/core-data/package.json index 947044f946a00e..a5b9572e275bcf 100644 --- a/packages/core-data/package.json +++ b/packages/core-data/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/core-data", - "version": "6.13.0-prerelease", + "version": "6.13.0", "description": "Access to and manipulation of core WordPress entities.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/create-block-tutorial-template/package.json b/packages/create-block-tutorial-template/package.json index 7aaede53696fc6..7ef3faad89e1d8 100644 --- a/packages/create-block-tutorial-template/package.json +++ b/packages/create-block-tutorial-template/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/create-block-tutorial-template", - "version": "2.24.0-prerelease", + "version": "2.24.0", "description": "Template for @wordpress/create-block used in the official WordPress tutorial.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/create-block/package.json b/packages/create-block/package.json index 350c6a9c313dfa..8a8bae31a334ec 100644 --- a/packages/create-block/package.json +++ b/packages/create-block/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/create-block", - "version": "4.20.0-prerelease", + "version": "4.20.0", "description": "Generates PHP, JS and CSS code for registering a block for a WordPress plugin.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/customize-widgets/package.json b/packages/customize-widgets/package.json index 73ad31da8d1b87..a9316080319531 100644 --- a/packages/customize-widgets/package.json +++ b/packages/customize-widgets/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/customize-widgets", - "version": "4.13.0-prerelease", + "version": "4.13.0", "description": "Widgets blocks in Customizer Module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/data-controls/package.json b/packages/data-controls/package.json index bc15cbcca7b371..277e13e080c7a1 100644 --- a/packages/data-controls/package.json +++ b/packages/data-controls/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/data-controls", - "version": "3.5.0-prerelease", + "version": "3.5.0", "description": "A set of common controls for the @wordpress/data api.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/data/package.json b/packages/data/package.json index 70534f32a736aa..b151115dca76d0 100644 --- a/packages/data/package.json +++ b/packages/data/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/data", - "version": "9.6.0-prerelease", + "version": "9.6.0", "description": "Data module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/date/package.json b/packages/date/package.json index 56d310213f7fff..f371b82c336035 100644 --- a/packages/date/package.json +++ b/packages/date/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/date", - "version": "4.36.0-prerelease", + "version": "4.36.0", "description": "Date module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/dependency-extraction-webpack-plugin/package.json b/packages/dependency-extraction-webpack-plugin/package.json index c32498d4790cfc..59f312b0fffd10 100644 --- a/packages/dependency-extraction-webpack-plugin/package.json +++ b/packages/dependency-extraction-webpack-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/dependency-extraction-webpack-plugin", - "version": "4.19.0-prerelease", + "version": "4.19.0", "description": "Extract WordPress script dependencies from webpack bundles.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/deprecated/package.json b/packages/deprecated/package.json index 939e4141b78779..c59a1acaa8a054 100644 --- a/packages/deprecated/package.json +++ b/packages/deprecated/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/deprecated", - "version": "3.36.0-prerelease", + "version": "3.36.0", "description": "Deprecation utility for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/docgen/package.json b/packages/docgen/package.json index 46de12f012b600..9809d771b596d3 100644 --- a/packages/docgen/package.json +++ b/packages/docgen/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/docgen", - "version": "1.45.0-prerelease", + "version": "1.45.0", "description": "Autogenerate public API documentation from exports and JSDoc comments.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/dom-ready/package.json b/packages/dom-ready/package.json index f12ea98ca29372..d839cc32a3c908 100644 --- a/packages/dom-ready/package.json +++ b/packages/dom-ready/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/dom-ready", - "version": "3.36.0-prerelease", + "version": "3.36.0", "description": "Execute callback after the DOM is loaded.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/dom/package.json b/packages/dom/package.json index 96621a58a874b9..6475b8844783fa 100644 --- a/packages/dom/package.json +++ b/packages/dom/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/dom", - "version": "3.36.0-prerelease", + "version": "3.36.0", "description": "DOM utilities module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/e2e-test-utils-playwright/package.json b/packages/e2e-test-utils-playwright/package.json index 1c3f4a01bd81e6..ab82d133566276 100644 --- a/packages/e2e-test-utils-playwright/package.json +++ b/packages/e2e-test-utils-playwright/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/e2e-test-utils-playwright", - "version": "0.4.0-prerelease", + "version": "0.4.0", "description": "End-To-End (E2E) test utils for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/e2e-test-utils/package.json b/packages/e2e-test-utils/package.json index 1575574a140e2d..69fa428544418b 100644 --- a/packages/e2e-test-utils/package.json +++ b/packages/e2e-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/e2e-test-utils", - "version": "10.7.0-prerelease", + "version": "10.7.0", "description": "End-To-End (E2E) test utils for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/e2e-tests/package.json b/packages/e2e-tests/package.json index 5fecc056b69240..de9108f1b59a97 100644 --- a/packages/e2e-tests/package.json +++ b/packages/e2e-tests/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/e2e-tests", - "version": "7.7.0-prerelease", + "version": "7.7.0", "description": "End-To-End (E2E) tests for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/edit-post/package.json b/packages/edit-post/package.json index db25dfd46fe578..d4f9cb16bd0502 100644 --- a/packages/edit-post/package.json +++ b/packages/edit-post/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/edit-post", - "version": "7.13.0-prerelease", + "version": "7.13.0", "description": "Edit Post module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/edit-site/package.json b/packages/edit-site/package.json index da9222302ae8d6..0b5560a277839e 100644 --- a/packages/edit-site/package.json +++ b/packages/edit-site/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/edit-site", - "version": "5.13.0-prerelease", + "version": "5.13.0", "description": "Edit Site Page module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/edit-widgets/package.json b/packages/edit-widgets/package.json index fef5fca923d26a..78a20158a44700 100644 --- a/packages/edit-widgets/package.json +++ b/packages/edit-widgets/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/edit-widgets", - "version": "5.13.0-prerelease", + "version": "5.13.0", "description": "Widgets Page module for WordPress..", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/editor/package.json b/packages/editor/package.json index 4477d0d3a1061c..89b6496bbadd79 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/editor", - "version": "13.13.0-prerelease", + "version": "13.13.0", "description": "Enhanced block editor for WordPress posts.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/element/package.json b/packages/element/package.json index 816d358b17d735..d40d57ed168fd4 100644 --- a/packages/element/package.json +++ b/packages/element/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/element", - "version": "5.13.0-prerelease", + "version": "5.13.0", "description": "Element React module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/env/package.json b/packages/env/package.json index 7c8ec2bb366ba9..67b12d17dfb56b 100644 --- a/packages/env/package.json +++ b/packages/env/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/env", - "version": "8.2.0-prerelease", + "version": "8.2.0", "description": "A zero-config, self contained local WordPress environment for development and testing.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/escape-html/package.json b/packages/escape-html/package.json index bcd89a63deb109..fbb2755e439ea2 100644 --- a/packages/escape-html/package.json +++ b/packages/escape-html/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/escape-html", - "version": "2.36.0-prerelease", + "version": "2.36.0", "description": "Escape HTML utils.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index d64f7a741ec95c..5dc7b4b7fe4820 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/eslint-plugin", - "version": "14.9.0-prerelease", + "version": "14.9.0", "description": "ESLint plugin for WordPress development.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/format-library/package.json b/packages/format-library/package.json index 29416e033899f6..902ac7fbbe36f1 100644 --- a/packages/format-library/package.json +++ b/packages/format-library/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/format-library", - "version": "4.13.0-prerelease", + "version": "4.13.0", "description": "Format library for the WordPress editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/hooks/package.json b/packages/hooks/package.json index a13f95e6ce3041..9cf064c801333b 100644 --- a/packages/hooks/package.json +++ b/packages/hooks/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/hooks", - "version": "3.36.0-prerelease", + "version": "3.36.0", "description": "WordPress hooks library.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/html-entities/package.json b/packages/html-entities/package.json index b1df9574c40b24..264c071d438a4b 100644 --- a/packages/html-entities/package.json +++ b/packages/html-entities/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/html-entities", - "version": "3.36.0-prerelease", + "version": "3.36.0", "description": "HTML entity utilities for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/i18n/package.json b/packages/i18n/package.json index 3ed704ef7f0b56..06281ebccc3216 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/i18n", - "version": "4.36.0-prerelease", + "version": "4.36.0", "description": "WordPress internationalization (i18n) library.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/icons/package.json b/packages/icons/package.json index d196e49bad084c..1ced62d92cfe45 100644 --- a/packages/icons/package.json +++ b/packages/icons/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/icons", - "version": "9.27.0-prerelease", + "version": "9.27.0", "description": "WordPress Icons package, based on dashicon.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/interface/package.json b/packages/interface/package.json index 8a48da0c37df95..76240850da4667 100644 --- a/packages/interface/package.json +++ b/packages/interface/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/interface", - "version": "5.13.0-prerelease", + "version": "5.13.0", "description": "Interface module for WordPress. The package contains shared functionality across the modern JavaScript-based WordPress screens.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/is-shallow-equal/package.json b/packages/is-shallow-equal/package.json index 47adb7388c29ed..c0acfa6352f26b 100644 --- a/packages/is-shallow-equal/package.json +++ b/packages/is-shallow-equal/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/is-shallow-equal", - "version": "4.36.0-prerelease", + "version": "4.36.0", "description": "Test for shallow equality between two objects or arrays.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/jest-console/package.json b/packages/jest-console/package.json index 73776a7fa25ae3..3fd951a9823778 100644 --- a/packages/jest-console/package.json +++ b/packages/jest-console/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/jest-console", - "version": "7.7.0-prerelease", + "version": "7.7.0", "description": "Custom Jest matchers for the Console object.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/jest-preset-default/package.json b/packages/jest-preset-default/package.json index 9eb973ecaacb53..e0e2ff33587e91 100644 --- a/packages/jest-preset-default/package.json +++ b/packages/jest-preset-default/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/jest-preset-default", - "version": "11.7.0-prerelease", + "version": "11.7.0", "description": "Default Jest preset for WordPress development.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/jest-puppeteer-axe/package.json b/packages/jest-puppeteer-axe/package.json index 21ba900b3bfae9..90936135de5634 100644 --- a/packages/jest-puppeteer-axe/package.json +++ b/packages/jest-puppeteer-axe/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/jest-puppeteer-axe", - "version": "6.7.0-prerelease", + "version": "6.7.0", "description": "Axe API integration with Jest and Puppeteer.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/keyboard-shortcuts/package.json b/packages/keyboard-shortcuts/package.json index 46b44604cb986b..dfa109be9276ee 100644 --- a/packages/keyboard-shortcuts/package.json +++ b/packages/keyboard-shortcuts/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/keyboard-shortcuts", - "version": "4.13.0-prerelease", + "version": "4.13.0", "description": "Handling keyboard shortcuts.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/keycodes/package.json b/packages/keycodes/package.json index 30337e4acc39f6..41acb05b0bf3e8 100644 --- a/packages/keycodes/package.json +++ b/packages/keycodes/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/keycodes", - "version": "3.36.0-prerelease", + "version": "3.36.0", "description": "Keycodes utilities for WordPress. Used to check for keyboard events across browsers/operating systems.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/lazy-import/package.json b/packages/lazy-import/package.json index 8e19a004f5d74f..d733cea00e2088 100644 --- a/packages/lazy-import/package.json +++ b/packages/lazy-import/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/lazy-import", - "version": "1.23.0-prerelease", + "version": "1.23.0", "description": "Lazily import a module, installing it automatically if missing.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/list-reusable-blocks/package.json b/packages/list-reusable-blocks/package.json index 0f8f86031b0075..f24fc355316ae5 100644 --- a/packages/list-reusable-blocks/package.json +++ b/packages/list-reusable-blocks/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/list-reusable-blocks", - "version": "4.13.0-prerelease", + "version": "4.13.0", "description": "Adding Export/Import support to the reusable blocks listing.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/media-utils/package.json b/packages/media-utils/package.json index 82d1d9f997285a..f44a65a7b74044 100644 --- a/packages/media-utils/package.json +++ b/packages/media-utils/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/media-utils", - "version": "4.27.0-prerelease", + "version": "4.27.0", "description": "WordPress Media Upload Utils.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/notices/package.json b/packages/notices/package.json index b25d2d07acd280..ca17e9c838bacc 100644 --- a/packages/notices/package.json +++ b/packages/notices/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/notices", - "version": "4.4.0-prerelease", + "version": "4.4.0", "description": "State management for notices.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/npm-package-json-lint-config/package.json b/packages/npm-package-json-lint-config/package.json index 006d276658b156..da423454a98a2a 100644 --- a/packages/npm-package-json-lint-config/package.json +++ b/packages/npm-package-json-lint-config/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/npm-package-json-lint-config", - "version": "4.21.0-prerelease", + "version": "4.21.0", "description": "WordPress npm-package-json-lint shareable configuration.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/plugins/package.json b/packages/plugins/package.json index 34252dcc1f18e7..063940e9f92154 100644 --- a/packages/plugins/package.json +++ b/packages/plugins/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/plugins", - "version": "6.4.0-prerelease", + "version": "6.4.0", "description": "Plugins module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/postcss-plugins-preset/package.json b/packages/postcss-plugins-preset/package.json index 39eb9c2cff32c3..307a6e31b080aa 100644 --- a/packages/postcss-plugins-preset/package.json +++ b/packages/postcss-plugins-preset/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/postcss-plugins-preset", - "version": "4.20.0-prerelease", + "version": "4.20.0", "description": "PostCSS sharable plugins preset for WordPress development.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/postcss-themes/package.json b/packages/postcss-themes/package.json index 4eabcdfcd43219..846bd4b61acddb 100644 --- a/packages/postcss-themes/package.json +++ b/packages/postcss-themes/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/postcss-themes", - "version": "5.19.0-prerelease", + "version": "5.19.0", "description": "PostCSS plugin to generate theme colors.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/preferences-persistence/package.json b/packages/preferences-persistence/package.json index 85d8425c3d5d0e..7ff80a6a8cce3a 100644 --- a/packages/preferences-persistence/package.json +++ b/packages/preferences-persistence/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/preferences-persistence", - "version": "1.28.0-prerelease", + "version": "1.28.0", "description": "Persistence utilities for `wordpress/preferences`.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/preferences/package.json b/packages/preferences/package.json index d014c36ef00ed4..ef1a1760fb5c0b 100644 --- a/packages/preferences/package.json +++ b/packages/preferences/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/preferences", - "version": "3.13.0-prerelease", + "version": "3.13.0", "description": "Utilities for managing WordPress preferences.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/prettier-config/package.json b/packages/prettier-config/package.json index a503ac304e4788..c2057f0ff54e1b 100644 --- a/packages/prettier-config/package.json +++ b/packages/prettier-config/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/prettier-config", - "version": "2.19.0-prerelease", + "version": "2.19.0", "description": "WordPress Prettier shared configuration.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/primitives/package.json b/packages/primitives/package.json index 95ba60284d8ba8..1b68a54a7aeca4 100644 --- a/packages/primitives/package.json +++ b/packages/primitives/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/primitives", - "version": "3.34.0-prerelease", + "version": "3.34.0", "description": "WordPress cross-platform primitives.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/priority-queue/package.json b/packages/priority-queue/package.json index a843a7c1d32610..601a6c9bac6e5a 100644 --- a/packages/priority-queue/package.json +++ b/packages/priority-queue/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/priority-queue", - "version": "2.36.0-prerelease", + "version": "2.36.0", "description": "Generic browser priority queue.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/private-apis/package.json b/packages/private-apis/package.json index 8d94f677fa340e..4fcef01c7c448c 100644 --- a/packages/private-apis/package.json +++ b/packages/private-apis/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/private-apis", - "version": "0.18.0-prerelease", + "version": "0.18.0", "description": "Internal experimental APIs for WordPress core.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/project-management-automation/package.json b/packages/project-management-automation/package.json index 30ca2998405ea0..43cc2325fa4438 100644 --- a/packages/project-management-automation/package.json +++ b/packages/project-management-automation/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/project-management-automation", - "version": "1.35.0-prerelease", + "version": "1.35.0", "description": "GitHub Action that implements various automation to assist with managing the Gutenberg GitHub repository.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/react-i18n/package.json b/packages/react-i18n/package.json index 56fe20d7f6fd78..b5e1243018867f 100644 --- a/packages/react-i18n/package.json +++ b/packages/react-i18n/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/react-i18n", - "version": "3.34.0-prerelease", + "version": "3.34.0", "description": "React bindings for @wordpress/i18n.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/readable-js-assets-webpack-plugin/package.json b/packages/readable-js-assets-webpack-plugin/package.json index 2eab266c74fb2b..756526d18281f9 100644 --- a/packages/readable-js-assets-webpack-plugin/package.json +++ b/packages/readable-js-assets-webpack-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/readable-js-assets-webpack-plugin", - "version": "2.19.0-prerelease", + "version": "2.19.0", "description": "Generate a readable JS file for each JS asset.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/redux-routine/package.json b/packages/redux-routine/package.json index 2a3fed69a3cc98..1672baa85df912 100644 --- a/packages/redux-routine/package.json +++ b/packages/redux-routine/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/redux-routine", - "version": "4.36.0-prerelease", + "version": "4.36.0", "description": "Redux middleware for generator coroutines.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/reusable-blocks/package.json b/packages/reusable-blocks/package.json index b92cb87360b71c..b32b856851b66c 100644 --- a/packages/reusable-blocks/package.json +++ b/packages/reusable-blocks/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/reusable-blocks", - "version": "4.13.0-prerelease", + "version": "4.13.0", "description": "Reusable blocks utilities.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/rich-text/package.json b/packages/rich-text/package.json index c1d73863f40f28..ea8cd72fa125e2 100644 --- a/packages/rich-text/package.json +++ b/packages/rich-text/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/rich-text", - "version": "6.13.0-prerelease", + "version": "6.13.0", "description": "Rich text value and manipulation API.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/router/package.json b/packages/router/package.json index 45ae63c2c8f49a..a62374207825b3 100644 --- a/packages/router/package.json +++ b/packages/router/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/router", - "version": "0.5.0-prerelease", + "version": "0.5.0", "description": "Router API for WordPress pages.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/scripts/package.json b/packages/scripts/package.json index 2728ca5165f3f9..bfa3d0403bf6da 100644 --- a/packages/scripts/package.json +++ b/packages/scripts/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/scripts", - "version": "26.7.0-prerelease", + "version": "26.7.0", "description": "Collection of reusable scripts for WordPress development.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/server-side-render/package.json b/packages/server-side-render/package.json index 5bc40a4fa9a7d4..2c82a64458d4ff 100644 --- a/packages/server-side-render/package.json +++ b/packages/server-side-render/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/server-side-render", - "version": "4.13.0-prerelease", + "version": "4.13.0", "description": "The component used with WordPress to server-side render a preview of dynamic blocks to display in the editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/shortcode/package.json b/packages/shortcode/package.json index 300bb259ca7c22..79c56c4ca2c256 100644 --- a/packages/shortcode/package.json +++ b/packages/shortcode/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/shortcode", - "version": "3.36.0-prerelease", + "version": "3.36.0", "description": "Shortcode module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/style-engine/package.json b/packages/style-engine/package.json index dd1a275c974246..7d49b7266ddc51 100644 --- a/packages/style-engine/package.json +++ b/packages/style-engine/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/style-engine", - "version": "1.19.0-prerelease", + "version": "1.19.0", "description": "A suite of parsers and compilers for WordPress styles.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/stylelint-config/package.json b/packages/stylelint-config/package.json index 03dab1655221d6..98bed0b8fce17e 100644 --- a/packages/stylelint-config/package.json +++ b/packages/stylelint-config/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/stylelint-config", - "version": "21.19.0-prerelease", + "version": "21.19.0", "description": "stylelint config for WordPress development.", "author": "The WordPress Contributors", "license": "MIT", diff --git a/packages/token-list/package.json b/packages/token-list/package.json index ce67241a6a34fc..8f8686312661fe 100644 --- a/packages/token-list/package.json +++ b/packages/token-list/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/token-list", - "version": "2.36.0-prerelease", + "version": "2.36.0", "description": "Constructable, plain JavaScript DOMTokenList implementation, supporting non-browser runtimes.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/url/package.json b/packages/url/package.json index a416310f2f8d16..53a4ecc7e9d522 100644 --- a/packages/url/package.json +++ b/packages/url/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/url", - "version": "3.37.0-prerelease", + "version": "3.37.0", "description": "WordPress URL utilities.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/viewport/package.json b/packages/viewport/package.json index 5a853bc2670098..4feee48f5be3ed 100644 --- a/packages/viewport/package.json +++ b/packages/viewport/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/viewport", - "version": "5.13.0-prerelease", + "version": "5.13.0", "description": "Viewport module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/warning/package.json b/packages/warning/package.json index 0ef3ad663b65bd..7f552e9f128f5d 100644 --- a/packages/warning/package.json +++ b/packages/warning/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/warning", - "version": "2.36.0-prerelease", + "version": "2.36.0", "description": "Warning utility for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/widgets/package.json b/packages/widgets/package.json index 102ec5d75fc22d..6663a97c749c97 100644 --- a/packages/widgets/package.json +++ b/packages/widgets/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/widgets", - "version": "3.13.0-prerelease", + "version": "3.13.0", "description": "Functionality used by the widgets block editor in the Widgets screen and the Customizer.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/wordcount/package.json b/packages/wordcount/package.json index 2e8071ec295f89..4f3ff6acd82f19 100644 --- a/packages/wordcount/package.json +++ b/packages/wordcount/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/wordcount", - "version": "3.36.0-prerelease", + "version": "3.36.0", "description": "WordPress word count utility.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", From d5ce58463c405cc51e30427e652416e8c06b96bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Grzegorz=20Zi=C3=B3=C5=82kowski?= Date: Fri, 30 Jun 2023 07:54:25 +0200 Subject: [PATCH 098/266] Docs: Move changelog entries to the unreleased section This is necessary because the latest npm release commits were cherry picked to trunk late in the workflow --- packages/components/CHANGELOG.md | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index d6b79db2d80521..5c87043b8e31fe 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -2,28 +2,34 @@ ## Unreleased -## 25.2.0 (2023-06-23) - ### Enhancements - `SelectControl`: Added option to set hidden options. ([#51545](https://github.com/WordPress/gutenberg/pull/51545)) -- `UnitControl`: Revamp support for changing unit by typing ([#39303](https://github.com/WordPress/gutenberg/pull/39303)). - `RangeControl`: Add `__next40pxDefaultSize` prop to opt into the new 40px default size ([#49105](https://github.com/WordPress/gutenberg/pull/49105)). -- `Modal`: Update corner radius to be between buttons and the site view frame, in a 2-4-8 system. ([#51254](https://github.com/WordPress/gutenberg/pull/51254)). - `Button`: Introduce `size` prop with `default`, `compact`, and `small` variants ([#51842](https://github.com/WordPress/gutenberg/pull/51842)). -- `ItemGroup`: Update button focus state styles to be inline with other button focus states in the editor. ([#51576](https://github.com/WordPress/gutenberg/pull/51576)). - `ItemGroup`: Update button focus state styles to target `:focus-visible` rather than `:focus`. ([#51787](https://github.com/WordPress/gutenberg/pull/51787)). ### Bug Fix -- `Popover`: Allow legitimate 0 positions to update popover position ([#51320](https://github.com/WordPress/gutenberg/pull/51320)). -- `Button`: Remove unnecessary margin from dashicon ([#51395](https://github.com/WordPress/gutenberg/pull/51395)). -- `Autocomplete`: Announce how many results are available to screen readers when suggestions list first renders ([#51018](https://github.com/WordPress/gutenberg/pull/51018)). - `ConfirmDialog`: Ensure onConfirm isn't called an extra time when submitting one of the buttons using the keyboard ([#51730](https://github.com/WordPress/gutenberg/pull/51730)). - `ZStack`: ZStack: fix component bounding box to match children ([#51836](https://github.com/WordPress/gutenberg/pull/51836)). - `Modal`: Add small top padding to the content so that avoid cutting off the visible outline when hovering items ([#51829](https://github.com/WordPress/gutenberg/pull/51829)). - `DropdownMenu`: fix icon style when dashicon is used ([#43574](https://github.com/WordPress/gutenberg/pull/43574)). +## 25.2.0 (2023-06-23) + +### Enhancements + +- `UnitControl`: Revamp support for changing unit by typing ([#39303](https://github.com/WordPress/gutenberg/pull/39303)). +- `Modal`: Update corner radius to be between buttons and the site view frame, in a 2-4-8 system. ([#51254](https://github.com/WordPress/gutenberg/pull/51254)). +- `ItemGroup`: Update button focus state styles to be inline with other button focus states in the editor. ([#51576](https://github.com/WordPress/gutenberg/pull/51576)). + +### Bug Fix + +- `Popover`: Allow legitimate 0 positions to update popover position ([#51320](https://github.com/WordPress/gutenberg/pull/51320)). +- `Button`: Remove unnecessary margin from dashicon ([#51395](https://github.com/WordPress/gutenberg/pull/51395)). +- `Autocomplete`: Announce how many results are available to screen readers when suggestions list first renders ([#51018](https://github.com/WordPress/gutenberg/pull/51018)). + ### Internal - `ClipboardButton`: Convert to TypeScript ([#51334](https://github.com/WordPress/gutenberg/pull/51334)). From 87b002d784694de63c9fdb21e406d76c8765bf42 Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Fri, 30 Jun 2023 15:09:17 +0900 Subject: [PATCH 099/266] Block Editor: Unify texts for Create pattern modal (#52151) --- .../reusable-block-convert-button.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-block-convert-button.js b/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-block-convert-button.js index e02cd4f428b679..d051e366412817 100644 --- a/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-block-convert-button.js +++ b/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-block-convert-button.js @@ -156,6 +156,7 @@ export default function ReusableBlockConvertButton( { label={ __( 'Name' ) } value={ title } onChange={ setTitle } + placeholder={ __( 'My pattern' ) } />
From 908fbff83379c40543b84cd5c8b67b189766e2e0 Mon Sep 17 00:00:00 2001 From: James Koster Date: Fri, 30 Jun 2023 07:14:18 +0100 Subject: [PATCH 100/266] Library: Update icons in the creation menu (#52108) --- packages/edit-site/src/components/add-new-pattern/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/edit-site/src/components/add-new-pattern/index.js b/packages/edit-site/src/components/add-new-pattern/index.js index ab309da1ab12d8..9a60c6555108d6 100644 --- a/packages/edit-site/src/components/add-new-pattern/index.js +++ b/packages/edit-site/src/components/add-new-pattern/index.js @@ -4,7 +4,7 @@ import { DropdownMenu } from '@wordpress/components'; import { useState } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; -import { plus, header, file } from '@wordpress/icons'; +import { plus, symbol, symbolFilled } from '@wordpress/icons'; import { privateApis as routerPrivateApis } from '@wordpress/router'; /** @@ -56,12 +56,12 @@ export default function AddNewPattern() { setShowTemplatePartModal( true ), title: __( 'Create template part' ), }, { - icon: file, + icon: symbol, onClick: () => setShowPatternModal( true ), title: __( 'Create pattern' ), }, From 32d4995e8560a8a17559012f167281a43d9ff4ba Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Fri, 30 Jun 2023 16:17:33 +1000 Subject: [PATCH 101/266] Patterns: Rename Library to Patterns (#52102) --- .../src/site-editor-navigation-commands.js | 2 +- .../components/create-pattern-modal/index.js | 2 +- .../src/components/page-main/index.js | 6 +- .../grid-item.js | 14 +-- .../{page-library => page-patterns}/grid.js | 2 +- .../{page-library => page-patterns}/index.js | 10 +- .../no-patterns.js | 2 +- .../patterns-list.js | 12 +-- .../search-items.js | 0 .../style.scss | 22 ++--- .../use-pattern-settings.js} | 2 +- .../use-patterns.js | 0 .../{page-library => page-patterns}/utils.js | 0 .../style.scss | 3 - .../sidebar-navigation-screen-main/index.js | 4 +- .../index.js | 2 +- .../category-item.js | 6 +- .../index.js | 16 +-- .../style.scss | 3 + .../use-default-pattern-categories.js | 0 .../use-my-patterns.js | 0 .../use-pattern-categories.js | 0 .../use-template-part-areas.js | 0 .../use-theme-patterns.js | 2 +- .../index.js | 2 +- .../index.js | 97 +++---------------- .../edit-site/src/components/sidebar/index.js | 6 +- .../use-sync-path-with-url.js | 2 +- packages/edit-site/src/style.scss | 4 +- .../edit-site/src/utils/get-is-list-page.js | 6 +- .../site-editor-url-navigation.spec.js | 2 +- 31 files changed, 81 insertions(+), 148 deletions(-) rename packages/edit-site/src/components/{page-library => page-patterns}/grid-item.js (92%) rename packages/edit-site/src/components/{page-library => page-patterns}/grid.js (94%) rename packages/edit-site/src/components/{page-library => page-patterns}/index.js (83%) rename packages/edit-site/src/components/{page-library => page-patterns}/no-patterns.js (77%) rename packages/edit-site/src/components/{page-library => page-patterns}/patterns-list.js (89%) rename packages/edit-site/src/components/{page-library => page-patterns}/search-items.js (100%) rename packages/edit-site/src/components/{page-library => page-patterns}/style.scss (82%) rename packages/edit-site/src/components/{page-library/use-library-settings.js => page-patterns/use-pattern-settings.js} (96%) rename packages/edit-site/src/components/{page-library => page-patterns}/use-patterns.js (100%) rename packages/edit-site/src/components/{page-library => page-patterns}/utils.js (100%) delete mode 100644 packages/edit-site/src/components/sidebar-navigation-screen-library/style.scss rename packages/edit-site/src/components/{sidebar-navigation-screen-library => sidebar-navigation-screen-patterns}/category-item.js (86%) rename packages/edit-site/src/components/{sidebar-navigation-screen-library => sidebar-navigation-screen-patterns}/index.js (94%) create mode 100644 packages/edit-site/src/components/sidebar-navigation-screen-patterns/style.scss rename packages/edit-site/src/components/{sidebar-navigation-screen-library => sidebar-navigation-screen-patterns}/use-default-pattern-categories.js (100%) rename packages/edit-site/src/components/{sidebar-navigation-screen-library => sidebar-navigation-screen-patterns}/use-my-patterns.js (100%) rename packages/edit-site/src/components/{sidebar-navigation-screen-library => sidebar-navigation-screen-patterns}/use-pattern-categories.js (100%) rename packages/edit-site/src/components/{sidebar-navigation-screen-library => sidebar-navigation-screen-patterns}/use-template-part-areas.js (100%) rename packages/edit-site/src/components/{sidebar-navigation-screen-library => sidebar-navigation-screen-patterns}/use-theme-patterns.js (96%) diff --git a/packages/core-commands/src/site-editor-navigation-commands.js b/packages/core-commands/src/site-editor-navigation-commands.js index 6331afd058de34..8f2003d0e08a27 100644 --- a/packages/core-commands/src/site-editor-navigation-commands.js +++ b/packages/core-commands/src/site-editor-navigation-commands.js @@ -203,7 +203,7 @@ function useSiteEditorBasicNavigationCommands() { icon: symbolFilled, callback: ( { close } ) => { const args = { - path: '/library', + path: '/patterns', }; const targetUrl = addQueryArgs( 'site-editor.php', args ); if ( isSiteEditor ) { diff --git a/packages/edit-site/src/components/create-pattern-modal/index.js b/packages/edit-site/src/components/create-pattern-modal/index.js index 2c7eb4599dee1a..7906cb2352c7b7 100644 --- a/packages/edit-site/src/components/create-pattern-modal/index.js +++ b/packages/edit-site/src/components/create-pattern-modal/index.js @@ -18,7 +18,7 @@ import { useDispatch } from '@wordpress/data'; /** * Internal dependencies */ -import { SYNC_TYPES, USER_PATTERN_CATEGORY } from '../page-library/utils'; +import { SYNC_TYPES, USER_PATTERN_CATEGORY } from '../page-patterns/utils'; export default function CreatePatternModal( { closeModal, diff --git a/packages/edit-site/src/components/page-main/index.js b/packages/edit-site/src/components/page-main/index.js index a6c05886888c45..af017a8db9700a 100644 --- a/packages/edit-site/src/components/page-main/index.js +++ b/packages/edit-site/src/components/page-main/index.js @@ -6,7 +6,7 @@ import { privateApis as routerPrivateApis } from '@wordpress/router'; /** * Internal dependencies */ -import PageLibrary from '../page-library'; +import PagePatterns from '../page-patterns'; import PageTemplateParts from '../page-template-parts'; import PageTemplates from '../page-templates'; import { unlock } from '../../lock-unlock'; @@ -22,8 +22,8 @@ export default function PageMain() { return ; } else if ( path === '/wp_template_part/all' ) { return ; - } else if ( path === '/library' ) { - return ; + } else if ( path === '/patterns' ) { + return ; } return null; diff --git a/packages/edit-site/src/components/page-library/grid-item.js b/packages/edit-site/src/components/page-patterns/grid-item.js similarity index 92% rename from packages/edit-site/src/components/page-library/grid-item.js rename to packages/edit-site/src/components/page-patterns/grid-item.js index 726f7a9896adc2..cc9d99fd900136 100644 --- a/packages/edit-site/src/components/page-library/grid-item.js +++ b/packages/edit-site/src/components/page-patterns/grid-item.js @@ -39,7 +39,7 @@ import { useLink } from '../routes/link'; export default function GridItem( { categoryId, composite, icon, item } ) { const instanceId = useInstanceId( GridItem ); - const descriptionId = `edit-site-library__pattern-description-${ instanceId }`; + const descriptionId = `edit-site-patterns__pattern-description-${ instanceId }`; const [ isDeleteDialogOpen, setIsDeleteDialogOpen ] = useState( false ); const { __experimentalDeleteReusableBlock } = @@ -61,10 +61,10 @@ export default function GridItem( { categoryId, composite, icon, item } ) { }; const isEmpty = ! item.blocks?.length; - const patternClassNames = classnames( 'edit-site-library__pattern', { + const patternClassNames = classnames( 'edit-site-patterns__pattern', { 'is-placeholder': isEmpty, } ); - const previewClassNames = classnames( 'edit-site-library__preview', { + const previewClassNames = classnames( 'edit-site-patterns__preview', { 'is-inactive': item.type === PATTERNS, } ); @@ -132,14 +132,14 @@ export default function GridItem( { categoryId, composite, icon, item } ) { ) }
+ +); +``` + +### Props + +The properties `isSelected`, `onBlur`, `onChange`, `onFocus`, `shouldDisplay`, `value`, `insertBlocksAfter` of this component are passed directly to their related props of its inner `` component ([see detailed info about the RichText component's props](https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/rich-text/README.md)). + +## Related components + +Caption components is mostly used by the [`BlockCaption`](https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/block-caption) component. \ No newline at end of file From 92dd32e7aaa26cf24e0e706c6481e834f67272c4 Mon Sep 17 00:00:00 2001 From: Tonya Mork Date: Mon, 10 Jul 2023 12:31:20 -0500 Subject: [PATCH 249/266] Font Face: to generate and print font-face styles for theme.json fonts (#51770) Introduces Font Face for generating and printing @font-face styles for fonts in theme.json merged data. It's minimalistic in what it publicly exposes to avoid BC concerns. There's no register, deregister, or enqueue of fonts or providers. The fonts to be processed come directly from theme.json merged data which contains the theme's defined fonts and user "activated" fonts (from the Font Library). This PR only loads Font Face into memory (thus replacing Fonts API) when the Fonts Library class. This design is intentional to ensure sites using Fonts API stay functioning until Fonts Library is introduced. --------- Co-authored-by: Anton Vlasenko Co-authored-by: Ari Stathopoulos --- ...class-wp-theme-json-resolver-gutenberg.php | 4 +- lib/compat/wordpress-6.3/script-loader.php | 1 - lib/experimental/fonts-api/fonts-api.php | 14 + .../fonts/class-wp-font-face-resolver.php | 158 +++++++ lib/experimental/fonts/class-wp-font-face.php | 418 ++++++++++++++++++ lib/experimental/fonts/fonts.php | 58 +++ lib/load.php | 19 +- phpunit/fonts/wp-font-face-testcase.php | 187 ++++++++ phpunit/fonts/wp-font-face-tests-dataset.php | 274 ++++++++++++ .../wpFontFace/generateAndPrint-test.php | 57 +++ .../getFontsFromThemeJson-test.php | 116 +++++ phpunit/fonts/wpPrintFontFaces-test.php | 75 ++++ 12 files changed, 1376 insertions(+), 5 deletions(-) create mode 100644 lib/experimental/fonts/class-wp-font-face-resolver.php create mode 100644 lib/experimental/fonts/class-wp-font-face.php create mode 100644 lib/experimental/fonts/fonts.php create mode 100644 phpunit/fonts/wp-font-face-testcase.php create mode 100644 phpunit/fonts/wp-font-face-tests-dataset.php create mode 100644 phpunit/fonts/wpFontFace/generateAndPrint-test.php create mode 100644 phpunit/fonts/wpFontFaceResolver/getFontsFromThemeJson-test.php create mode 100644 phpunit/fonts/wpPrintFontFaces-test.php diff --git a/lib/class-wp-theme-json-resolver-gutenberg.php b/lib/class-wp-theme-json-resolver-gutenberg.php index 6e9d05cd7f238b..1e825e3c6bbe4f 100644 --- a/lib/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/class-wp-theme-json-resolver-gutenberg.php @@ -275,7 +275,9 @@ public static function get_theme_data( $deprecated = array(), $options = array() } // BEGIN OF EXPERIMENTAL CODE. Not to backport to core. - static::$theme = WP_Fonts_Resolver::add_missing_fonts_to_theme_json( static::$theme ); + if ( ! class_exists( 'WP_Font_Face' ) && class_exists( 'WP_Fonts_Resolver' ) ) { + static::$theme = WP_Fonts_Resolver::add_missing_fonts_to_theme_json( static::$theme ); + } // END OF EXPERIMENTAL CODE. } diff --git a/lib/compat/wordpress-6.3/script-loader.php b/lib/compat/wordpress-6.3/script-loader.php index c515eb10fdc6bb..8f7bda2a648114 100644 --- a/lib/compat/wordpress-6.3/script-loader.php +++ b/lib/compat/wordpress-6.3/script-loader.php @@ -81,7 +81,6 @@ function _gutenberg_get_iframed_editor_assets() { ob_start(); wp_print_styles(); - wp_print_fonts( true ); $styles = ob_get_clean(); ob_start(); diff --git a/lib/experimental/fonts-api/fonts-api.php b/lib/experimental/fonts-api/fonts-api.php index e2ad6e1cb53af2..8d07dc118f56e1 100644 --- a/lib/experimental/fonts-api/fonts-api.php +++ b/lib/experimental/fonts-api/fonts-api.php @@ -243,3 +243,17 @@ static function( $mime_types ) { * during the build. See: tools/webpack/blocks.js. */ add_action( 'init', 'WP_Fonts_Resolver::register_fonts_from_theme_json', 21 ); + +add_filter( + 'block_editor_settings_all', + static function( $settings ) { + ob_start(); + wp_print_fonts( true ); + $styles = ob_get_clean(); + + // Add the font-face styles to iframed editor assets. + $settings['__unstableResolvedAssets']['styles'] .= $styles; + return $settings; + }, + 11 +); diff --git a/lib/experimental/fonts/class-wp-font-face-resolver.php b/lib/experimental/fonts/class-wp-font-face-resolver.php new file mode 100644 index 00000000000000..16e74d6051aa74 --- /dev/null +++ b/lib/experimental/fonts/class-wp-font-face-resolver.php @@ -0,0 +1,158 @@ +get_settings(); + + // Bail out early if there are no font settings. + if ( empty( $settings['typography'] ) || empty( $settings['typography']['fontFamilies'] ) ) { + return array(); + } + + return static::parse_settings( $settings ); + } + + /** + * Parse theme.json settings to extract font definitions with variations grouped by font-family. + * + * @since X.X.X + * + * @param array $settings Font settings to parse. + * @return array Returns an array of fonts, grouped by font-family. + */ + private static function parse_settings( array $settings ) { + $fonts = array(); + + foreach ( $settings['typography']['fontFamilies'] as $font_families ) { + foreach ( $font_families as $definition ) { + + // Skip if font-family "name" is not defined. + if ( empty( $definition['name'] ) ) { + continue; + } + + // Skip if "fontFace" is not defined, meaning there are no variations. + if ( empty( $definition['fontFace'] ) ) { + continue; + } + + $font_family = $definition['name']; + + // Prepare the fonts array structure for this font-family. + if ( ! array_key_exists( $font_family, $fonts ) ) { + $fonts[ $font_family ] = array(); + } + + $fonts[ $font_family ] = static::convert_font_face_properties( $definition['fontFace'], $font_family ); + } + } + + return $fonts; + } + + /** + * Converts font-face properties from theme.json format. + * + * @since X.X.X + * + * @param array $font_face_definition The font-face definitions to convert. + * @param string $font_family_property The value to store in the font-face font-family property. + * @return array Converted font-face properties. + */ + private static function convert_font_face_properties( array $font_face_definition, $font_family_property ) { + $converted_font_faces = array(); + + foreach ( $font_face_definition as $font_face ) { + // Add the font-family property to the font-face. + $font_face['font-family'] = $font_family_property; + + // Converts the "file:./" src placeholder into a theme font file URI. + if ( ! empty( $font_face['src'] ) ) { + $font_face['src'] = static::to_theme_file_uri( (array) $font_face['src'] ); + } + + // Convert camelCase properties into kebab-case. + $font_face = static::to_kebab_case( $font_face ); + + $converted_font_faces[] = $font_face; + } + + return $converted_font_faces; + } + + /** + * Converts each 'file:./' placeholder into a URI to the font file in the theme. + * + * The 'file:./' is specified in the theme's `theme.json` as a placeholder to be + * replaced with the URI to the font file's location in the theme. When a "src" + * beings with this placeholder, it is replaced, converting the src into a URI. + * + * @since X.X.X + * + * @param array $src An array of font file sources to process. + * @return array An array of font file src URI(s). + */ + private static function to_theme_file_uri( array $src ) { + $placeholder = 'file:./'; + + foreach ( $src as $src_key => $src_url ) { + // Skip if the src doesn't start with the placeholder, as there's nothing to replace. + if ( ! str_starts_with( $src_url, $placeholder ) ) { + continue; + } + + $src_file = str_replace( $placeholder, '', $src_url ); + $src[ $src_key ] = get_theme_file_uri( $src_file ); + } + + return $src; + } + + /** + * Converts all first dimension keys into kebab-case. + * + * @since X.X.X + * + * @param array $data The array to process. + * @return array Data with first dimension keys converted into kebab-case. + */ + private static function to_kebab_case( array $data ) { + foreach ( $data as $key => $value ) { + $kebab_case = _wp_to_kebab_case( $key ); + $data[ $kebab_case ] = $value; + if ( $kebab_case !== $key ) { + unset( $data[ $key ] ); + } + } + + return $data; + } +} diff --git a/lib/experimental/fonts/class-wp-font-face.php b/lib/experimental/fonts/class-wp-font-face.php new file mode 100644 index 00000000000000..482bf4d42396d3 --- /dev/null +++ b/lib/experimental/fonts/class-wp-font-face.php @@ -0,0 +1,418 @@ + '', + 'font-style' => 'normal', + 'font-weight' => '400', + 'font-display' => 'fallback', + ); + + /** + * Valid font-face property names. + * + * @since X.X.X + * + * @var string[] + */ + private $valid_font_face_properties = array( + 'ascent-override', + 'descent-override', + 'font-display', + 'font-family', + 'font-stretch', + 'font-style', + 'font-weight', + 'font-variant', + 'font-feature-settings', + 'font-variation-settings', + 'line-gap-override', + 'size-adjust', + 'src', + 'unicode-range', + ); + + /** + * Valid font-display values. + * + * @since X.X.X + * + * @var string[] + */ + private $valid_font_display = array( 'auto', 'block', 'fallback', 'swap', 'optional' ); + + /** + * Array of font-face style tag's attribute(s) + * where the key is the attribute name and the + * value is its value. + * + * @since X.X.X + * + * @var string[] + */ + private $style_tag_attrs = array(); + + /** + * Creates and initializes an instance of WP_Font_Face. + * + * @since X.X.X + */ + public function __construct() { + /** + * Filters the font-face property defaults. + * + * @since X.X.X + * + * @param array $defaults { + * An array of required font-face properties and defaults. + * + * @type string $provider The provider ID. Default 'local'. + * @type string $font-family The font-family property. Default empty string. + * @type string $font-style The font-style property. Default 'normal'. + * @type string $font-weight The font-weight property. Default '400'. + * @type string $font-display The font-display property. Default 'fallback'. + * } + */ + $this->font_face_property_defaults = apply_filters( 'wp_font_face_property_defaults', $this->font_face_property_defaults ); + + if ( + function_exists( 'is_admin' ) && ! is_admin() + && + function_exists( 'current_theme_supports' ) && ! current_theme_supports( 'html5', 'style' ) + ) { + $this->style_tag_attrs = array( 'type' => 'text/css' ); + } + } + + /** + * Generates and prints the `@font-face` styles for the given fonts. + * + * @since X.X.X + * + * @param array $fonts The fonts to generate and print @font-face styles. + */ + public function generate_and_print( array $fonts ) { + $fonts = $this->validate_fonts( $fonts ); + + // Bail out if there are no fonts are given to process. + if ( empty( $fonts ) ) { + return; + } + + printf( + $this->get_style_element(), + $this->get_css( $fonts ) + ); + } + + /** + * Validates each of the font-face properties. + * + * @since X.X.X + * + * @param array $fonts The fonts to valid. + * @return array Prepared font-faces organized by provider and font-family. + */ + private function validate_fonts( array $fonts ) { + $validated_fonts = array(); + + foreach ( $fonts as $font_faces ) { + foreach ( $font_faces as $font_face ) { + $font_face = $this->validate_font_face_properties( $font_face ); + // Skip if failed validation. + if ( false === $font_face ) { + continue; + } + + $validated_fonts[] = $font_face; + } + } + + return $validated_fonts; + } + + /** + * Validates each font-face property. + * + * @since X.X.X + * + * @param array $font_face Font face properties to validate. + * @return false|array Validated font-face on success. Else, false. + */ + private function validate_font_face_properties( array $font_face ) { + $font_face = wp_parse_args( $font_face, $this->font_face_property_defaults ); + + // Check the font-family. + if ( empty( $font_face['font-family'] ) || ! is_string( $font_face['font-family'] ) ) { + trigger_error( 'Font font-family must be a non-empty string.' ); + return false; + } + + // Make sure that local fonts have 'src' defined. + if ( empty( $font_face['src'] ) || ( ! is_string( $font_face['src'] ) && ! is_array( $font_face['src'] ) ) ) { + trigger_error( 'Font src must be a non-empty string or an array of strings.' ); + return false; + } + + // Validate the 'src' property. + if ( ! empty( $font_face['src'] ) ) { + foreach ( (array) $font_face['src'] as $src ) { + if ( empty( $src ) || ! is_string( $src ) ) { + trigger_error( 'Each font src must be a non-empty string.' ); + return false; + } + } + } + + // Check the font-weight. + if ( ! is_string( $font_face['font-weight'] ) && ! is_int( $font_face['font-weight'] ) ) { + trigger_error( 'Font font-weight must be a properly formatted string or integer.' ); + return false; + } + + // Check the font-display. + if ( ! in_array( $font_face['font-display'], $this->valid_font_display, true ) ) { + $font_face['font-display'] = $this->font_face_property_defaults['font-display']; + } + + // Remove invalid properties. + foreach ( $font_face as $prop => $value ) { + if ( ! in_array( $prop, $this->valid_font_face_properties, true ) ) { + unset( $font_face[ $prop ] ); + } + } + + return $font_face; + } + + /** + * Gets the `\n"; + } + + /** + * Gets the defined \n"; + $expected_output = sprintf( $style_element, $expected ); + + $this->expectOutputString( $expected_output ); + $font_face->generate_and_print( $fonts ); + } +} diff --git a/phpunit/fonts/wpFontFaceResolver/getFontsFromThemeJson-test.php b/phpunit/fonts/wpFontFaceResolver/getFontsFromThemeJson-test.php new file mode 100644 index 00000000000000..74c0f0a4f3c423 --- /dev/null +++ b/phpunit/fonts/wpFontFaceResolver/getFontsFromThemeJson-test.php @@ -0,0 +1,116 @@ +assertIsArray( $fonts, 'Should return an array data type' ); + $this->assertEmpty( $fonts, 'Should return an empty array' ); + } + + public function test_should_return_all_fonts_from_theme() { + switch_theme( static::FONTS_THEME ); + + $actual = WP_Font_Face_Resolver::get_fonts_from_theme_json(); + $expected = $this->get_expected_fonts_for_fonts_block_theme( 'fonts' ); + $this->assertSame( $expected, $actual ); + } + + /** + * @dataProvider data_should_replace_src_file_placeholder + * + * @param string $font_name Font's name. + * @param string $font_index Font's index in the $fonts array. + * @param string $expected Expected src. + */ + public function test_should_replace_src_file_placeholder( $font_name, $font_index, $expected ) { + switch_theme( static::FONTS_THEME ); + + $fonts = WP_Font_Face_Resolver::get_fonts_from_theme_json(); + + $actual = $fonts[ $font_name ][ $font_index ]['src'][0]; + $expected = get_stylesheet_directory_uri() . $expected; + + $this->assertStringNotContainsString( 'file:./', $actual, 'Font src should not contain the "file:./" placeholder' ); + $this->assertSame( $expected, $actual, 'Font src should be an URL to its file' ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_should_replace_src_file_placeholder() { + return array( + // Theme's theme.json. + 'DM Sans: 400 normal' => array( + 'font_name' => 'DM Sans', + 'font_index' => 0, + 'expected' => '/assets/fonts/dm-sans/DMSans-Regular.woff2', + ), + 'DM Sans: 400 italic' => array( + 'font_name' => 'DM Sans', + 'font_index' => 1, + 'expected' => '/assets/fonts/dm-sans/DMSans-Regular-Italic.woff2', + ), + 'DM Sans: 700 normal' => array( + 'font_name' => 'DM Sans', + 'font_index' => 2, + 'expected' => '/assets/fonts/dm-sans/DMSans-Bold.woff2', + ), + 'DM Sans: 700 italic' => array( + 'font_name' => 'DM Sans', + 'font_index' => 3, + 'expected' => '/assets/fonts/dm-sans/DMSans-Bold-Italic.woff2', + ), + 'Source Serif Pro: 200-900 normal' => array( + 'font_name' => 'Source Serif Pro', + 'font_index' => 0, + 'expected' => '/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + ), + 'Source Serif Pro: 200-900 italic' => array( + 'font_name' => 'Source Serif Pro', + 'font_index' => 1, + 'expected' => '/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2', + ), + ); + } +} diff --git a/phpunit/fonts/wpPrintFontFaces-test.php b/phpunit/fonts/wpPrintFontFaces-test.php new file mode 100644 index 00000000000000..aa44dd4f71b0d6 --- /dev/null +++ b/phpunit/fonts/wpPrintFontFaces-test.php @@ -0,0 +1,75 @@ +expectOutputString( '' ); + wp_print_font_faces(); + } + + /** + * @dataProvider data_should_print_given_fonts + * + * @param array $fonts Fonts to process. + * @param string $expected Expected CSS. + */ + public function test_should_print_given_fonts( array $fonts, $expected ) { + $expected_output = $this->get_expected_styles_output( $expected ); + + $this->expectOutputString( $expected_output ); + wp_print_font_faces( $fonts ); + } + + public function test_should_print_fonts_in_merged_data() { + switch_theme( static::FONTS_THEME ); + + $expected = $this->get_expected_fonts_for_fonts_block_theme( 'font_face_styles' ); + $expected_output = $this->get_expected_styles_output( $expected ); + + $this->expectOutputString( $expected_output ); + wp_print_font_faces(); + } + + private function get_expected_styles_output( $styles ) { + $style_element = "\n"; + return sprintf( $style_element, $styles ); + } +} From 7b6c18433d44a49c261049cb3830d46017aae932 Mon Sep 17 00:00:00 2001 From: Phill <38789408+SavPhill@users.noreply.github.com> Date: Tue, 11 Jul 2023 01:01:46 +0700 Subject: [PATCH 250/266] Removed line break within the code block (#46920) --- docs/explanations/architecture/styles.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/explanations/architecture/styles.md b/docs/explanations/architecture/styles.md index 5b147b2bbe6eab..a8a5af72fec76b 100644 --- a/docs/explanations/architecture/styles.md +++ b/docs/explanations/architecture/styles.md @@ -59,10 +59,8 @@ The user may change the state of this block by applying different styles: a text After some user modifications to the block, the initial markup may become something like this: ```html -

+

``` This is what we refer to as "user-provided block styles", also know as "local styles" or "serialized styles". Essentially, each tool (font size, color, etc) ends up adding some classes and/or inline styles to the block markup. The CSS styling for these classes is part of the block, global, or theme stylesheets. From ba0a3320c878c36a9a3cc27660ca0b9b3d89facd Mon Sep 17 00:00:00 2001 From: Ella <4710635+ellatrix@users.noreply.github.com> Date: Mon, 10 Jul 2023 21:05:46 +0200 Subject: [PATCH 251/266] Iframe: avoid asset parsing & fix script localisation (#52405) * Iframe: avoid asset parsing & fix script localisation * Add e2e test for script localisation --- .../src/components/iframe/index.js | 63 +++++-------------- .../plugins/iframed-enqueue-block-assets.php | 13 ++++ .../iframed-enqueue-block-assets/script.js | 3 + .../iframed-equeue-block-assets.test.js | 7 +++ 4 files changed, 39 insertions(+), 47 deletions(-) create mode 100644 packages/e2e-tests/plugins/iframed-enqueue-block-assets/script.js diff --git a/packages/block-editor/src/components/iframe/index.js b/packages/block-editor/src/components/iframe/index.js index 4d4521eae35814..afe403af0aa392 100644 --- a/packages/block-editor/src/components/iframe/index.js +++ b/packages/block-editor/src/components/iframe/index.js @@ -11,7 +11,6 @@ import { createPortal, forwardRef, useMemo, - useReducer, useEffect, } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; @@ -78,29 +77,6 @@ function bubbleEvents( doc ) { } } -function useParsedAssets( html ) { - return useMemo( () => { - const doc = document.implementation.createHTMLDocument( '' ); - doc.body.innerHTML = html; - return Array.from( doc.body.children ); - }, [ html ] ); -} - -async function loadScript( head, { id, src } ) { - return new Promise( ( resolve, reject ) => { - const script = head.ownerDocument.createElement( 'script' ); - script.id = id; - if ( src ) { - script.src = src; - script.onload = () => resolve(); - script.onerror = () => reject(); - } else { - resolve(); - } - head.appendChild( script ); - } ); -} - function Iframe( { contentRef, children, @@ -112,21 +88,22 @@ function Iframe( { forwardedRef: ref, ...props } ) { - const assets = useSelect( + const { styles = '', scripts = '' } = useSelect( ( select ) => select( blockEditorStore ).getSettings().__unstableResolvedAssets, [] ); - const [ , forceRender ] = useReducer( () => ( {} ) ); const [ iframeDocument, setIframeDocument ] = useState(); const [ bodyClasses, setBodyClasses ] = useState( [] ); const compatStyles = useCompatibilityStyles(); - const scripts = useParsedAssets( assets?.scripts ); const clearerRef = useBlockSelectionClearer(); const [ before, writingFlowRef, after ] = useWritingFlow(); const [ contentResizeListener, { height: contentHeight } ] = useResizeObserver(); const setRef = useRefEffect( ( node ) => { + node._load = () => { + setIframeDocument( node.contentDocument ); + }; let iFrameDocument; // Prevent the default browser action for files dropped outside of dropzones. function preventFileDropDefault( event ) { @@ -138,7 +115,6 @@ function Iframe( { iFrameDocument = contentDocument; bubbleEvents( contentDocument ); - setIframeDocument( contentDocument ); clearerRef( documentElement ); // Ideally ALL classes that are added through get_body_class should @@ -154,7 +130,6 @@ function Iframe( { ); contentDocument.dir = ownerDocument.dir; - documentElement.removeChild( contentDocument.body ); for ( const compatStyle of compatStyles ) { if ( contentDocument.getElementById( compatStyle.id ) ) { @@ -199,35 +174,29 @@ function Iframe( { }; }, [] ); - const headRef = useRefEffect( ( element ) => { - scripts - .reduce( - ( promise, script ) => - promise.then( () => loadScript( element, script ) ), - Promise.resolve() - ) - .finally( () => { - // When script are loaded, re-render blocks to allow them - // to initialise. - forceRender(); - } ); - }, [] ); const disabledRef = useDisabled( { isDisabled: ! readonly } ); const bodyRef = useMergeRefs( [ contentRef, clearerRef, writingFlowRef, disabledRef, - headRef, ] ); // Correct doctype is required to enable rendering in standards // mode. Also preload the styles to avoid a flash of unstyled // content. - const html = - '' + - '' + - ( assets?.styles ?? '' ); + const html = ` + + + + + ${ styles } + ${ scripts } + + + + +`; const [ src, cleanup ] = useMemo( () => { const _src = URL.createObjectURL( diff --git a/packages/e2e-tests/plugins/iframed-enqueue-block-assets.php b/packages/e2e-tests/plugins/iframed-enqueue-block-assets.php index ad98354dd45dc7..3f24a6e25cfcb5 100644 --- a/packages/e2e-tests/plugins/iframed-enqueue-block-assets.php +++ b/packages/e2e-tests/plugins/iframed-enqueue-block-assets.php @@ -17,5 +17,18 @@ static function() { filemtime( plugin_dir_path( __FILE__ ) . 'iframed-enqueue-block-assets/style.css' ) ); wp_add_inline_style( 'iframed-enqueue-block-assets', 'body{padding:20px!important}' ); + wp_enqueue_script( + 'iframed-enqueue-block-assets-script', + plugin_dir_url( __FILE__ ) . 'iframed-enqueue-block-assets/script.js', + array(), + filemtime( plugin_dir_path( __FILE__ ) . 'iframed-enqueue-block-assets/script.js' ) + ); + wp_localize_script( + 'iframed-enqueue-block-assets-script', + 'iframedEnqueueBlockAssetsL10n', + array( + 'test' => 'Iframed Enqueue Block Assets!', + ) + ); } ); diff --git a/packages/e2e-tests/plugins/iframed-enqueue-block-assets/script.js b/packages/e2e-tests/plugins/iframed-enqueue-block-assets/script.js new file mode 100644 index 00000000000000..f0eddd65c70ebe --- /dev/null +++ b/packages/e2e-tests/plugins/iframed-enqueue-block-assets/script.js @@ -0,0 +1,3 @@ +window.addEventListener( 'load', () => { + document.body.dataset.iframedEnqueueBlockAssetsL10n = window.iframedEnqueueBlockAssetsL10n.test; +} ); diff --git a/packages/e2e-tests/specs/editor/plugins/iframed-equeue-block-assets.test.js b/packages/e2e-tests/specs/editor/plugins/iframed-equeue-block-assets.test.js index c1bd26fe1c7610..c29af593abb124 100644 --- a/packages/e2e-tests/specs/editor/plugins/iframed-equeue-block-assets.test.js +++ b/packages/e2e-tests/specs/editor/plugins/iframed-equeue-block-assets.test.js @@ -32,6 +32,7 @@ describe( 'iframed inline styles', () => { } ); it( 'should load styles added through enqueue_block_assets', async () => { + await page.waitForSelector( 'iframe[name="editor-canvas"]' ); // Check stylesheet. expect( await getComputedStyle( canvas(), 'body', 'background-color' ) @@ -40,5 +41,11 @@ describe( 'iframed inline styles', () => { expect( await getComputedStyle( canvas(), 'body', 'padding' ) ).toBe( '20px' ); + + expect( + await canvas().evaluate( () => ( { ...document.body.dataset } ) ) + ).toEqual( { + iframedEnqueueBlockAssetsL10n: 'Iframed Enqueue Block Assets!', + } ); } ); } ); From dabe35463a073368ab9e2aa2bc1a607c395aa9e3 Mon Sep 17 00:00:00 2001 From: Gutenberg Repository Automation Date: Mon, 10 Jul 2023 19:22:31 +0000 Subject: [PATCH 252/266] Bump plugin version to 16.2.0-rc.2 --- gutenberg.php | 2 +- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gutenberg.php b/gutenberg.php index c90c1d0bb22e89..b1513d52e61d1b 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.1 * Requires PHP: 5.6 - * Version: 16.2.0-rc.1 + * Version: 16.2.0-rc.2 * Author: Gutenberg Team * Text Domain: gutenberg * diff --git a/package-lock.json b/package-lock.json index a0a00a13e45201..60cabfe3244bfc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "16.2.0-rc.1", + "version": "16.2.0-rc.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index a7da15a6671821..46fb91477bd6eb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "16.2.0-rc.1", + "version": "16.2.0-rc.2", "private": true, "description": "A new WordPress editor experience.", "author": "The WordPress Contributors", From a045f9ec5d8ec9efd186f484ca3fc4d050abb44c Mon Sep 17 00:00:00 2001 From: Gutenberg Repository Automation Date: Mon, 10 Jul 2023 19:50:38 +0000 Subject: [PATCH 253/266] Update Changelog for 16.2.0-rc.2 --- changelog.txt | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/changelog.txt b/changelog.txt index 1937dcdf193768..561df06f235299 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,30 @@ == Changelog == += 16.2.0-rc.2 = + +## Changelog + +### Bug Fixes + +#### Block Library +- RichText/Footnotes: Make getRichTextValues work with InnerBlocks.Content. ([52241](https://github.com/WordPress/gutenberg/pull/52241)) + +#### Blocks +- Post and Comment Template blocks: Change render_block_context priority to 1 (https://github.com/WordPress/gutenberg/pull/52364) +- Footnotes: Fix incorrect anchor position in Firefox (https://github.com/WordPress/gutenberg/pull/52425) +- Footnotes: fix lingering format boundary attr (https://github.com/WordPress/gutenberg/pull/52439) +- Footnotes: save numbering through the entity provider (https://github.com/WordPress/gutenberg/pull/52423) + +#### Code Quality / Performance +- Iframe: avoid asset parsing & fix script localisation + +## Contributors + +The following contributors merged PRs in this release: + +@ellatrix @ockham @t-hamano + + = 16.1.2 = ### Bug fixes From c1f97cb90d17458a5a44a9be4e76c40f09558386 Mon Sep 17 00:00:00 2001 From: Marcelo Serpa <81248+fullofcaffeine@users.noreply.github.com> Date: Mon, 10 Jul 2023 14:10:50 -0600 Subject: [PATCH 254/266] (readme.md) Document the new process for releasing point releases for old release branches (#49968) * Document the new process for releasing point releases for old release branches/versions of Gutenberg * Document a bit better * Add a couple of minor improvements * Fix use-case example, the description of the logic was wrong * Make it more concise * Improve last paragraph * Update docs/contributors/code/release.md Co-authored-by: Bernie Reiter <96308+ockham@users.noreply.github.com> --------- Co-authored-by: Bernie Reiter <96308+ockham@users.noreply.github.com> --- docs/contributors/code/release.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/contributors/code/release.md b/docs/contributors/code/release.md index 180de30d195571..edbd67655a6990 100644 --- a/docs/contributors/code/release.md +++ b/docs/contributors/code/release.md @@ -181,7 +181,13 @@ _If_ however, the previous release was an **RC** (e.g. `X.Y.0-rc.1`) you will ne To do this, when running the Workflow, select the appropriate `release/` branch from the `Use workflow from` dropdown (e.g. `release/12.5`) and specify `stable` in the text input field. -Please note you **cannot create minor releases for previous stable releases once a more recent stable release has been published** as this would require significant changes to how we upload plugin versions to the WP.org plugin SVN repo). Always check the latest release version before you proceed (see [this Issue](https://github.com/WordPress/gutenberg/issues/33277#issuecomment-876289457) for more information). +##### Creating a minor release for previous stable releases + +It is possible to create a minor release for any release branch even after a more recent stable release has been published. This can be done for _any_ previous release branches, allowing more flexibility in delivering updates to users. In the past, users had to wait for the next stable release, potentially taking days. Now, fixes can be swiftly shipped to any previous release branches as required. + +The process is identical to the one documented above when an RC is already out: choose a previous release branch, type `stable`, and click "Run workflow". The release will be published on the GitHub releases page for Gutenberg and to the WordPress core repository SVN as a `tag` under http://plugins.svn.wordpress.org/gutenberg/tags/. The SVN `trunk` directory will not be touched. + +**IMPORTANT:** When publishing the draft created by the ["Build Plugin Zip" workflow](https://github.com/WordPress/gutenberg/actions/workflows/build-plugin-zip.yml), make sure to leave the "Set as last release" checkbox unchecked. If it is left checked by accident, the ["Upload Gutenberg plugin to WordPress.org plugin" workflow](https://github.com/WordPress/gutenberg/actions/workflows/upload-release-to-plugin-repo.yml) will still correctly upload it **as a tag (and will _not_ replace the `trunk` version)** to the WordPress plugin repository SVN - the workflow will perform some version arithmetic to determine how the plugin should be shipped - but you'll still need to fix the state on GitHub by setting the right release as `latest` on the [releases](https://github.com/WordPress/gutenberg/releases/) page! #### Troubleshooting From 05f75a964931be3e332f6706329e9e676ec00e95 Mon Sep 17 00:00:00 2001 From: James Koster Date: Tue, 11 Jul 2023 00:01:43 +0100 Subject: [PATCH 255/266] Update descriptions (#52468) --- .../edit-site/src/components/page-patterns/patterns-list.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/edit-site/src/components/page-patterns/patterns-list.js b/packages/edit-site/src/components/page-patterns/patterns-list.js index bc2a18bf394567..7bf2a9d5065841 100644 --- a/packages/edit-site/src/components/page-patterns/patterns-list.js +++ b/packages/edit-site/src/components/page-patterns/patterns-list.js @@ -40,10 +40,10 @@ const SYNC_FILTERS = { const SYNC_DESCRIPTIONS = { all: '', [ SYNC_TYPES.full ]: __( - 'Patterns that are kept in sync across your site.' + 'Patterns that are kept in sync across the site.' ), [ SYNC_TYPES.unsynced ]: __( - 'Patterns that can be changed freely without affecting your site.' + 'Patterns that can be changed freely without affecting the site.' ), }; From f448947625ada1c2360fa8ae9d03d1c3df8b7584 Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Tue, 11 Jul 2023 08:56:13 +0900 Subject: [PATCH 256/266] Ensure that the unsaved title is not persisted when reopening the modal (#52473) --- .../template-actions/rename-menu-item.js | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/packages/edit-site/src/components/template-actions/rename-menu-item.js b/packages/edit-site/src/components/template-actions/rename-menu-item.js index 9c6368ced17bf8..9f897fcd2e9433 100644 --- a/packages/edit-site/src/components/template-actions/rename-menu-item.js +++ b/packages/edit-site/src/components/template-actions/rename-menu-item.js @@ -17,10 +17,8 @@ import { store as noticesStore } from '@wordpress/notices'; import { decodeEntities } from '@wordpress/html-entities'; export default function RenameMenuItem( { template, onClose } ) { - const [ title, setTitle ] = useState( - decodeEntities( template.title.rendered ) - ); - + const title = decodeEntities( template.title.rendered ); + const [ editedTitle, setEditedTitle ] = useState( title ); const [ isModalOpen, setIsModalOpen ] = useState( false ); const { @@ -39,11 +37,11 @@ export default function RenameMenuItem( { template, onClose } ) { try { await editEntityRecord( 'postType', template.type, template.id, { - title, + title: editedTitle, } ); // Update state before saving rerenders the list. - setTitle( '' ); + setEditedTitle( '' ); setIsModalOpen( false ); onClose(); @@ -73,7 +71,12 @@ export default function RenameMenuItem( { template, onClose } ) { return ( <> - setIsModalOpen( true ) }> + { + setIsModalOpen( true ); + setEditedTitle( title ); + } } + > { __( 'Rename' ) } { isModalOpen && ( @@ -89,8 +92,8 @@ export default function RenameMenuItem( { template, onClose } ) { From b802534433013b292406291925d976f6ea371989 Mon Sep 17 00:00:00 2001 From: Gutenberg Repository Automation Date: Tue, 11 Jul 2023 00:24:16 +0000 Subject: [PATCH 257/266] Bump plugin version to 16.2.0-rc.3 --- gutenberg.php | 2 +- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gutenberg.php b/gutenberg.php index b1513d52e61d1b..65b712ac1f76a3 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.1 * Requires PHP: 5.6 - * Version: 16.2.0-rc.2 + * Version: 16.2.0-rc.3 * Author: Gutenberg Team * Text Domain: gutenberg * diff --git a/package-lock.json b/package-lock.json index 60cabfe3244bfc..f4d914d70969a0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "16.2.0-rc.2", + "version": "16.2.0-rc.3", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 46fb91477bd6eb..9956d97e11016b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "16.2.0-rc.2", + "version": "16.2.0-rc.3", "private": true, "description": "A new WordPress editor experience.", "author": "The WordPress Contributors", From 488b40d264d14216abc199cf15ccc74ffaab8aab Mon Sep 17 00:00:00 2001 From: Gutenberg Repository Automation Date: Tue, 11 Jul 2023 00:34:39 +0000 Subject: [PATCH 258/266] Update Changelog for 16.2.0-rc.3 --- changelog.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/changelog.txt b/changelog.txt index 561df06f235299..2904601f889553 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,15 @@ == Changelog == += 16.2.0-rc.3 = + +## Changelog + +### Bug Fixes + +#### Build Tooling +- Revert phpcs testVersion back to PHP 5.6. ([52384](https://github.com/WordPress/gutenberg/pull/52384)) + + = 16.2.0-rc.2 = ## Changelog From 2937e5666c175dea15d44fd1479ccf96c6240260 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor?= <27339341+priethor@users.noreply.github.com> Date: Tue, 11 Jul 2023 03:16:07 +0200 Subject: [PATCH 259/266] Revert "Post editor: Require confirmation before removing Footnotes (#52277)" (#52486) This reverts commit e6426ea60b99ae76f703baeb22c8add5cc7afbf4. --- .../block-removal-warning-modal/index.js | 3 --- packages/edit-post/src/components/layout/index.js | 15 +-------------- packages/edit-site/src/components/editor/index.js | 3 --- 3 files changed, 1 insertion(+), 20 deletions(-) diff --git a/packages/block-editor/src/components/block-removal-warning-modal/index.js b/packages/block-editor/src/components/block-removal-warning-modal/index.js index af521b7233435a..08f3deccb5ae08 100644 --- a/packages/block-editor/src/components/block-removal-warning-modal/index.js +++ b/packages/block-editor/src/components/block-removal-warning-modal/index.js @@ -50,9 +50,6 @@ export function BlockRemovalWarningModal( { rules } ) { { blockNamesForPrompt.length === 1 ? (

{ rules[ blockNamesForPrompt[ 0 ] ] }

diff --git a/packages/edit-post/src/components/layout/index.js b/packages/edit-post/src/components/layout/index.js index 0c18521b1215f2..15bc017900daa2 100644 --- a/packages/edit-post/src/components/layout/index.js +++ b/packages/edit-post/src/components/layout/index.js @@ -16,10 +16,7 @@ import { store as editorStore, } from '@wordpress/editor'; import { useSelect, useDispatch } from '@wordpress/data'; -import { - BlockBreadcrumb, - privateApis as blockEditorPrivateApis, -} from '@wordpress/block-editor'; +import { BlockBreadcrumb } from '@wordpress/block-editor'; import { Button, ScrollLock, Popover } from '@wordpress/components'; import { useViewportMatch } from '@wordpress/compose'; import { PluginArea } from '@wordpress/plugins'; @@ -52,9 +49,6 @@ import WelcomeGuide from '../welcome-guide'; import ActionsPanel from './actions-panel'; import StartPageOptions from '../start-page-options'; import { store as editPostStore } from '../../store'; -import { unlock } from '../../lock-unlock'; - -const { BlockRemovalWarningModal } = unlock( blockEditorPrivateApis ); const interfaceLabels = { /* translators: accessibility text for the editor top bar landmark region. */ @@ -69,12 +63,6 @@ const interfaceLabels = { footer: __( 'Editor footer' ), }; -const blockRemovalRules = { - 'core/footnotes': __( - 'The Footnotes block displays all footnotes found in the content. Note that any footnotes in the content will persist after removing this block.' - ), -}; - function Layout( { styles } ) { const isMobileViewport = useViewportMatch( 'medium', '<' ); const isHugeViewport = useViewportMatch( 'huge', '>=' ); @@ -214,7 +202,6 @@ function Layout( { styles } ) { - Date: Tue, 11 Jul 2023 13:35:29 +1000 Subject: [PATCH 260/266] Rename block theme activation nonce variable. (#52398) --- lib/compat/wordpress-6.3/theme-previews.php | 2 +- packages/edit-site/src/utils/use-activate-theme.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/compat/wordpress-6.3/theme-previews.php b/lib/compat/wordpress-6.3/theme-previews.php index 31d684c17cf2a3..26153d74878b58 100644 --- a/lib/compat/wordpress-6.3/theme-previews.php +++ b/lib/compat/wordpress-6.3/theme-previews.php @@ -107,7 +107,7 @@ function block_theme_activate_nonce() { $nonce_handle = 'switch-theme_' . gutenberg_get_theme_preview_path(); ?> Date: Tue, 11 Jul 2023 13:59:27 +1000 Subject: [PATCH 261/266] remove status icon (#52457) --- .../status-label.js | 36 +------------------ 1 file changed, 1 insertion(+), 35 deletions(-) diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-page/status-label.js b/packages/edit-site/src/components/sidebar-navigation-screen-page/status-label.js index bcfc540b1f841d..f864d48de33834 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-page/status-label.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-page/status-label.js @@ -9,42 +9,10 @@ import classnames from 'classnames'; import { __, sprintf } from '@wordpress/i18n'; import { dateI18n, getDate, humanTimeDiff } from '@wordpress/date'; import { createInterpolateElement } from '@wordpress/element'; -import { Path, SVG } from '@wordpress/primitives'; - -const publishedIcon = ( - - - -); - -const draftIcon = ( - - - -); - -const pendingIcon = ( - - - -); export default function StatusLabel( { status, date, short } ) { const relateToNow = humanTimeDiff( date ); let statusLabel = status; - let statusIcon = pendingIcon; switch ( status ) { case 'publish': statusLabel = date @@ -57,7 +25,6 @@ export default function StatusLabel( { status, date, short } ) { { time: