Add the list view component and button to the modal editor (#38809)
* Add document overview opened state to the EditorContext * Create document overview toolbar button * Add document overview button to the header toolbar * Create document overview sidebar * Register document overview sidebar styles * Set document overview sidebar visible when document overview button is pressed * Add changelog file
This commit is contained in:
parent
7860415be5
commit
ecd1795b44
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: add
|
||||
|
||||
Add the list view component and button to the modal editor
|
|
@ -6,8 +6,10 @@ import { createContext } from '@wordpress/element';
|
|||
type EditorContextType = {
|
||||
hasRedo: boolean;
|
||||
hasUndo: boolean;
|
||||
isDocumentOverviewOpened: boolean;
|
||||
isInserterOpened: boolean;
|
||||
redo: () => void;
|
||||
setIsDocumentOverviewOpened: ( value: boolean ) => void;
|
||||
setIsInserterOpened: ( value: boolean ) => void;
|
||||
undo: () => void;
|
||||
};
|
||||
|
@ -15,8 +17,10 @@ type EditorContextType = {
|
|||
export const EditorContext = createContext< EditorContextType >( {
|
||||
hasRedo: false,
|
||||
hasUndo: false,
|
||||
isDocumentOverviewOpened: false,
|
||||
isInserterOpened: false,
|
||||
redo: () => {},
|
||||
setIsDocumentOverviewOpened: () => {},
|
||||
setIsInserterOpened: () => {},
|
||||
undo: () => {},
|
||||
} );
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { Button } from '@wordpress/components';
|
||||
import { createElement, forwardRef, useContext } from '@wordpress/element';
|
||||
import { listView as listViewIcon } from '@wordpress/icons';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { Ref } from 'react';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { DocumentOverviewProps } from './types';
|
||||
import { EditorContext } from '../../context';
|
||||
|
||||
export const DocumentOverview = forwardRef(
|
||||
function ForwardedRefDocumentOverview(
|
||||
props: DocumentOverviewProps,
|
||||
ref: Ref< HTMLButtonElement >
|
||||
) {
|
||||
const { isDocumentOverviewOpened, setIsDocumentOverviewOpened } =
|
||||
useContext( EditorContext );
|
||||
|
||||
function handleClick() {
|
||||
setIsDocumentOverviewOpened( ! isDocumentOverviewOpened );
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
{ ...props }
|
||||
ref={ ref }
|
||||
icon={ listViewIcon }
|
||||
isPressed={ isDocumentOverviewOpened }
|
||||
/* translators: button label text should, if possible, be under 16 characters. */
|
||||
label={ __( 'Document overview', 'woocommerce' ) }
|
||||
onClick={ handleClick }
|
||||
className="document-overview"
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
|
@ -0,0 +1,2 @@
|
|||
export * from './document-overview';
|
||||
export * from './types';
|
|
@ -0,0 +1 @@
|
|||
export type DocumentOverviewProps = { [ key: string ]: unknown };
|
|
@ -31,6 +31,7 @@ import { Button, ToolbarItem } from '@wordpress/components';
|
|||
import { EditorContext } from '../context';
|
||||
import EditorHistoryRedo from './editor-history-redo';
|
||||
import EditorHistoryUndo from './editor-history-undo';
|
||||
import { DocumentOverview } from './document-overview';
|
||||
|
||||
export function HeaderToolbar() {
|
||||
const { isInserterOpened, setIsInserterOpened } =
|
||||
|
@ -114,6 +115,7 @@ export function HeaderToolbar() {
|
|||
) }
|
||||
<ToolbarItem as={ EditorHistoryUndo } />
|
||||
<ToolbarItem as={ EditorHistoryRedo } />
|
||||
<ToolbarItem as={ DocumentOverview } />
|
||||
</>
|
||||
) }
|
||||
</div>
|
||||
|
|
|
@ -53,6 +53,7 @@ export function IframeEditor( {
|
|||
setBlocks,
|
||||
} );
|
||||
const [ isInserterOpened, setIsInserterOpened ] = useState( false );
|
||||
const [ isListViewOpened, setIsListViewOpened ] = useState( false );
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore This action exists in the block editor store.
|
||||
const { clearSelectedBlock, updateSettings } =
|
||||
|
@ -80,8 +81,10 @@ export function IframeEditor( {
|
|||
hasRedo,
|
||||
hasUndo,
|
||||
isInserterOpened,
|
||||
isDocumentOverviewOpened: isListViewOpened,
|
||||
redo,
|
||||
setIsInserterOpened,
|
||||
setIsDocumentOverviewOpened: setIsListViewOpened,
|
||||
undo,
|
||||
} }
|
||||
>
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { Button, TabPanel } from '@wordpress/components';
|
||||
import {
|
||||
useFocusOnMount,
|
||||
useFocusReturn,
|
||||
useMergeRefs,
|
||||
} from '@wordpress/compose';
|
||||
import {
|
||||
createElement,
|
||||
useRef,
|
||||
useState,
|
||||
useContext,
|
||||
} from '@wordpress/element';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { closeSmall } from '@wordpress/icons';
|
||||
import {
|
||||
// @ts-expect-error Module "@wordpress/block-editor" has no exported member '__experimentalListView'
|
||||
__experimentalListView as ListView,
|
||||
} from '@wordpress/block-editor';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { EditorContext } from '../../context';
|
||||
|
||||
export function DocumentOverviewSidebar() {
|
||||
const { setIsDocumentOverviewOpened: setIsListViewOpened } =
|
||||
useContext( EditorContext );
|
||||
|
||||
// This hook handles focus when the sidebar first renders.
|
||||
const focusOnMountRef = useFocusOnMount( 'firstElement' );
|
||||
// The next 2 hooks handle focus for when the sidebar closes and returning focus to the element that had focus before sidebar opened.
|
||||
const headerFocusReturnRef = useFocusReturn();
|
||||
const contentFocusReturnRef = useFocusReturn();
|
||||
|
||||
function closeOnEscape( event: React.KeyboardEvent< HTMLDivElement > ) {
|
||||
if ( event.code === 'Escape' && ! event.defaultPrevented ) {
|
||||
event.preventDefault();
|
||||
setIsListViewOpened( false );
|
||||
}
|
||||
}
|
||||
|
||||
// Use internal state instead of a ref to make sure that the component
|
||||
// re-renders when the dropZoneElement updates.
|
||||
const [ dropZoneElement, setDropZoneElement ] = useState( null );
|
||||
// Tracks our current tab.
|
||||
const [ tab, setTab ] = useState( 'list-view' );
|
||||
|
||||
// This ref refers to the list view application area.
|
||||
const listViewRef = useRef< HTMLDivElement >( null );
|
||||
|
||||
// Must merge the refs together so focus can be handled properly in the next function.
|
||||
const listViewContainerRef = useMergeRefs( [
|
||||
contentFocusReturnRef,
|
||||
focusOnMountRef,
|
||||
listViewRef,
|
||||
setDropZoneElement,
|
||||
] );
|
||||
|
||||
/**
|
||||
* Render tab content for a given tab name.
|
||||
*
|
||||
* @param tabName The name of the tab to render.
|
||||
*/
|
||||
function renderTabContent( tabName: string ) {
|
||||
if ( tabName === 'list-view' ) {
|
||||
return <ListView dropZoneElement={ dropZoneElement } />;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
|
||||
<div
|
||||
className="woocommerce-iframe-editor__document-overview-sidebar"
|
||||
onKeyDown={ closeOnEscape }
|
||||
>
|
||||
<Button
|
||||
className="woocommerce-iframe-editor__document-overview-sidebar-close-button"
|
||||
ref={ headerFocusReturnRef }
|
||||
icon={ closeSmall }
|
||||
label={ __( 'Close', 'woocommerce' ) }
|
||||
onClick={ () => setIsListViewOpened( false ) }
|
||||
/>
|
||||
<TabPanel
|
||||
className="woocommerce-iframe-editor__document-overview-sidebar-tab-panel"
|
||||
initialTabName={ tab }
|
||||
onSelect={ setTab }
|
||||
tabs={ [
|
||||
{
|
||||
name: 'list-view',
|
||||
title: 'List View',
|
||||
className:
|
||||
'woocommerce-iframe-editor__document-overview-sidebar-tab-item',
|
||||
},
|
||||
] }
|
||||
>
|
||||
{ ( currentTab ) => (
|
||||
<div
|
||||
className="woocommerce-iframe-editor__document-overview-sidebar-tab-content"
|
||||
ref={ listViewContainerRef }
|
||||
>
|
||||
{ renderTabContent( currentTab.name ) }
|
||||
</div>
|
||||
) }
|
||||
</TabPanel>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export * from './document-overview-sidebar';
|
|
@ -0,0 +1,39 @@
|
|||
.woocommerce-iframe-editor__document-overview-sidebar {
|
||||
position: relative;
|
||||
width: 350px;
|
||||
height: 100%;
|
||||
border-right: 1px solid $gray-400;
|
||||
|
||||
&-close-button {
|
||||
position: absolute;
|
||||
right: calc($grid-unit - 2px);
|
||||
top: calc($grid-unit - 2px);
|
||||
}
|
||||
|
||||
&-tab-panel {
|
||||
height: 100%;
|
||||
|
||||
.components-tab-panel__tabs {
|
||||
padding-right: $grid-unit-60;
|
||||
border-bottom: 1px solid $gray-400;
|
||||
}
|
||||
|
||||
.components-tab-panel__tab-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: calc(100% - $grid-unit-60);
|
||||
}
|
||||
}
|
||||
|
||||
&-tab-content {
|
||||
height: 100%;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
padding: $grid-unit calc($grid-unit - 2px);
|
||||
|
||||
.block-editor-list-view-tree {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,13 +8,19 @@ import { createElement, useContext } from '@wordpress/element';
|
|||
*/
|
||||
import { EditorContext } from '../context';
|
||||
import InserterSidebar from './inserter-sidebar';
|
||||
import { DocumentOverviewSidebar } from './document-overview-sidebar';
|
||||
|
||||
export function SecondarySidebar() {
|
||||
const { isInserterOpened } = useContext( EditorContext );
|
||||
const { isInserterOpened, isDocumentOverviewOpened: isListViewOpened } =
|
||||
useContext( EditorContext );
|
||||
|
||||
if ( isInserterOpened ) {
|
||||
return <InserterSidebar />;
|
||||
}
|
||||
|
||||
if ( isListViewOpened ) {
|
||||
return <DocumentOverviewSidebar />;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
@import './iframe-editor.scss';
|
||||
@import './header-toolbar/header-toolbar.scss';
|
||||
@import './resize-handle.scss';
|
||||
@import './back-button.scss';
|
||||
@import './secondary-sidebar/inserter-sidebar.scss';
|
||||
@import "./iframe-editor.scss";
|
||||
@import "./header-toolbar/header-toolbar.scss";
|
||||
@import "./resize-handle.scss";
|
||||
@import "./back-button.scss";
|
||||
@import "./secondary-sidebar/inserter-sidebar.scss";
|
||||
@import "./secondary-sidebar/document-overview-sidebar/style.scss";
|
||||
|
|
Loading…
Reference in New Issue