diff --git a/packages/block-editor/src/components/inserter/menu.js b/packages/block-editor/src/components/inserter/menu.js index 6a4ac798b7490..bcd60a687a36f 100644 --- a/packages/block-editor/src/components/inserter/menu.js +++ b/packages/block-editor/src/components/inserter/menu.js @@ -30,8 +30,8 @@ import { PatternCategoryPreviewPanel } from './block-patterns-tab/pattern-catego import { MediaTab, MediaCategoryPanel } from './media-tab'; import InserterSearchResults from './search-results'; import useInsertionPoint from './hooks/use-insertion-point'; -import InserterTabs from './tabs'; import { store as blockEditorStore } from '../../store'; +import TabbedSidebar from '../tabbed-sidebar'; const NOOP = () => {}; function InserterMenu( @@ -315,21 +315,49 @@ function InserterMenu( ref={ ref } >
- - { inserterSearch } - { selectedTab === 'blocks' && - ! delayedFilterValue && - blocksTab } - { selectedTab === 'patterns' && - ! delayedFilterValue && - patternsTab } - { selectedTab === 'media' && mediaTab } - + closeButtonLabel={ __( 'Close block inserter' ) } + tabs={ [ + { + name: 'blocks', + title: __( 'Blocks' ), + panel: ( + <> + { inserterSearch } + { selectedTab === 'blocks' && + ! delayedFilterValue && + blocksTab } + + ), + }, + { + name: 'patterns', + title: __( 'Patterns' ), + panel: ( + <> + { inserterSearch } + { selectedTab === 'patterns' && + ! delayedFilterValue && + patternsTab } + + ), + }, + { + name: 'media', + title: __( 'Media' ), + panel: ( + <> + { inserterSearch } + { mediaTab } + + ), + }, + ] } + />
{ showInserterHelpPanel && hoveredItem && ( ( + , + panelRef: useRef('an-optional-ref'), + }, + { + name: 'slug-2', + title: _x( 'Title 2', 'context' ), + panel: , + }, + ] } + onClose={ onClickCloseButton } + onSelect={ onSelectTab } + defaultTabId="slug-1" + ref={ tabsRef } + /> +); +``` + +### Props + +### `defaultTabId` + +- **Type:** `String` +- **Default:** `undefined` + +This is passed to the `Tabs` component so it can handle the tab to select by default when it component renders. + +### `onClose` + +- **Type:** `Function` + +The function that is called when the close button is clicked. + +### `onSelect` + +- **Type:** `Function` + +This is passed to the `Tabs` component - it will be called when a tab has been selected. It is passed the selected tab's ID as an argument. + +### `selectedTab` + +- **Type:** `String` +- **Default:** `undefined` + +This is passed to the `Tabs` component - it will display this tab as selected. + +### `tabs` + +- **Type:** `Array` +- **Default:** `undefined` + +An array of tabs which will be rendered as `TabList` and `TabPanel` components. diff --git a/packages/block-editor/src/components/tabbed-sidebar/index.js b/packages/block-editor/src/components/tabbed-sidebar/index.js new file mode 100644 index 0000000000000..a0cb510c72090 --- /dev/null +++ b/packages/block-editor/src/components/tabbed-sidebar/index.js @@ -0,0 +1,70 @@ +/** + * WordPress dependencies + */ +import { + Button, + privateApis as componentsPrivateApis, +} from '@wordpress/components'; +import { forwardRef } from '@wordpress/element'; +import { closeSmall } from '@wordpress/icons'; + +/** + * Internal dependencies + */ +import { unlock } from '../../lock-unlock'; + +const { Tabs } = unlock( componentsPrivateApis ); + +function TabbedSidebar( + { defaultTabId, onClose, onSelect, selectedTab, tabs, closeButtonLabel }, + ref +) { + return ( +
+ +
+
+ { tabs.map( ( tab ) => ( + + { tab.panel } + + ) ) } +
+
+ ); +} + +export default forwardRef( TabbedSidebar ); diff --git a/packages/block-editor/src/components/tabbed-sidebar/style.scss b/packages/block-editor/src/components/tabbed-sidebar/style.scss new file mode 100644 index 0000000000000..e392cf955ed06 --- /dev/null +++ b/packages/block-editor/src/components/tabbed-sidebar/style.scss @@ -0,0 +1,53 @@ +.block-editor-tabbed-sidebar { + height: 100%; + display: flex; + flex-direction: column; + flex-grow: 1; + overflow: hidden; + + @include break-medium() { + width: 350px; + } +} + +.block-editor-tabbed-sidebar__tablist-and-close-button { + border-bottom: $border-width solid $gray-300; + display: flex; + justify-content: space-between; + padding-right: $grid-unit-15; +} + + +.block-editor-tabbed-sidebar__close-button { + background: $white; + /* stylelint-disable-next-line property-disallowed-list -- This should be removed when https://github.com/WordPress/gutenberg/issues/59013 is fixed. */ + order: 1; + align-self: center; +} + +.block-editor-tabbed-sidebar__tablist { + box-sizing: border-box; + flex-grow: 1; + margin-bottom: -$border-width; + width: 100%; +} + +.block-editor-tabbed-sidebar__tab { + flex-grow: 1; + margin-bottom: -$border-width; + + &[id$="reusable"] { + flex-grow: inherit; + // These are to align the `reusable` icon with the search icon. + padding-left: $grid-unit-20; + padding-right: $grid-unit-20; + } +} + +.block-editor-tabbed-sidebar__tabpanel { + display: flex; + flex-grow: 1; + flex-direction: column; + overflow-y: auto; + scrollbar-gutter: auto; +} diff --git a/packages/block-editor/src/private-apis.js b/packages/block-editor/src/private-apis.js index bfa6ac0c90c84..44f68670556a3 100644 --- a/packages/block-editor/src/private-apis.js +++ b/packages/block-editor/src/private-apis.js @@ -48,6 +48,7 @@ import { PrivateInserterLibrary } from './components/inserter/library'; import { PrivatePublishDateTimePicker } from './components/publish-date-time-picker'; import useSpacingSizes from './components/spacing-sizes-control/hooks/use-spacing-sizes'; import useBlockDisplayTitle from './components/block-title/use-block-display-title'; +import TabbedSidebar from './components/tabbed-sidebar'; /** * Private @wordpress/block-editor APIs. @@ -77,6 +78,7 @@ lock( privateApis, { useLayoutStyles, DimensionsTool, ResolutionTool, + TabbedSidebar, TextAlignmentControl, ReusableBlocksRenameHint, useReusableBlocksRenameHint, diff --git a/packages/block-editor/src/style.scss b/packages/block-editor/src/style.scss index 0772ddf0d31e5..b2e753bea986e 100644 --- a/packages/block-editor/src/style.scss +++ b/packages/block-editor/src/style.scss @@ -42,6 +42,7 @@ @import "./components/rich-text/style.scss"; @import "./components/segmented-text-control/style.scss"; @import "./components/skip-to-selected-block/style.scss"; +@import "./components/tabbed-sidebar/style.scss"; @import "./components/tool-selector/style.scss"; @import "./components/url-input/style.scss"; @import "./components/url-popover/style.scss"; diff --git a/packages/editor/src/components/list-view-sidebar/index.js b/packages/editor/src/components/list-view-sidebar/index.js index 79a63ecdec4d9..c90479c23ec70 100644 --- a/packages/editor/src/components/list-view-sidebar/index.js +++ b/packages/editor/src/components/list-view-sidebar/index.js @@ -1,17 +1,15 @@ /** * WordPress dependencies */ -import { __experimentalListView as ListView } from '@wordpress/block-editor'; import { - Button, - privateApis as componentsPrivateApis, -} from '@wordpress/components'; + __experimentalListView as ListView, + privateApis as blockEditorPrivateApis, +} from '@wordpress/block-editor'; import { useFocusOnMount, useMergeRefs } from '@wordpress/compose'; import { useDispatch, useSelect } from '@wordpress/data'; import { focus } from '@wordpress/dom'; import { useCallback, useRef, useState } from '@wordpress/element'; import { __, _x } from '@wordpress/i18n'; -import { closeSmall } from '@wordpress/icons'; import { useShortcut } from '@wordpress/keyboard-shortcuts'; import { ESCAPE } from '@wordpress/keycodes'; @@ -22,7 +20,7 @@ import ListViewOutline from './list-view-outline'; import { unlock } from '../../lock-unlock'; import { store as editorStore } from '../../store'; -const { Tabs } = unlock( componentsPrivateApis ); +const { TabbedSidebar } = unlock( blockEditorPrivateApis ); export default function ListViewSidebar() { const { setIsListViewOpened } = useDispatch( editorStore ); @@ -120,64 +118,38 @@ export default function ListViewSidebar() { onKeyDown={ closeOnEscape } ref={ sidebarRef } > - +
+ +
+ + ), + panelRef: listViewContainerRef, + }, + { + name: 'outline', + title: _x( 'Outline', 'Post overview' ), + panel: ( +
+ +
+ ), + }, + ] } + onClose={ closeListView } onSelect={ ( tabName ) => setTab( tabName ) } - selectOnMove={ false } - // The initial tab value is set explicitly to avoid an initial - // render where no tab is selected. This ensures that the - // tabpanel height is correct so the relevant scroll container - // can be rendered internally. defaultTabId="list-view" - > -
-
- - -
-
- -
-
-
- -
- -
-
-
+ ref={ tabsRef } + closeButtonLabel={ __( 'Close' ) } + /> ); } diff --git a/packages/editor/src/components/list-view-sidebar/style.scss b/packages/editor/src/components/list-view-sidebar/style.scss index 973defca41f1c..3bf56b2c80760 100644 --- a/packages/editor/src/components/list-view-sidebar/style.scss +++ b/packages/editor/src/components/list-view-sidebar/style.scss @@ -1,39 +1,5 @@ .editor-list-view-sidebar { height: 100%; - display: flex; - flex-direction: column; - - @include break-medium() { - // Same width as the Inserter. - // @see packages/block-editor/src/components/inserter/style.scss - width: 350px; - } - .editor-list-view-sidebar__header { - display: flex; - border-bottom: $border-width solid $gray-300; - } - .editor-list-view-sidebar__close-button { - background: $white; - /* stylelint-disable-next-line property-disallowed-list -- This should be removed when https://github.com/WordPress/gutenberg/issues/59013 is fixed. */ - order: 1; - align-self: center; - margin-right: $grid-unit-15; - } -} - -.editor-list-view-sidebar__tabs-tablist { - box-sizing: border-box; - flex-grow: 1; - -} - -.editor-list-view-sidebar__tabs-tab { - width: 50%; - margin-bottom: -$border-width; -} - -.editor-list-view-sidebar__tabs-tabpanel { - height: calc(100% - #{$grid-unit-60 - $border-width}); } .editor-list-view-sidebar__list-view-panel-content,