UNPKG

@wordpress/editor

Version:
281 lines (260 loc) 7.99 kB
/** * WordPress dependencies */ import { __ } from '@wordpress/i18n'; import { useSelect, useDispatch } from '@wordpress/data'; import { __experimentalVStack as VStack } from '@wordpress/components'; import { useRef } from '@wordpress/element'; import { useViewportMatch } from '@wordpress/compose'; import { useShortcut } from '@wordpress/keyboard-shortcuts'; import { comment as commentIcon } from '@wordpress/icons'; import { store as blockEditorStore } from '@wordpress/block-editor'; import { store as interfaceStore } from '@wordpress/interface'; import { store as preferencesStore } from '@wordpress/preferences'; /** * Internal dependencies */ import PluginSidebar from '../plugin-sidebar'; import { ALL_NOTES_SIDEBAR, FLOATING_NOTES_SIDEBAR, SIDEBARS, } from './constants'; import { Comments } from './comments'; import { store as editorStore } from '../../store'; import AddCommentMenuItem from './comment-menu-item'; import CommentAvatarIndicator from './comment-indicator-toolbar'; import { useGlobalStylesContext } from '../global-styles-provider'; import { useBlockComments, useBlockCommentsActions, useEnableFloatingSidebar, } from './hooks'; import PostTypeSupportCheck from '../post-type-support-check'; import { unlock } from '../../lock-unlock'; function NotesSidebarContent( { styles, comments, commentSidebarRef, reflowComments, commentLastUpdated, isFloating = false, } ) { const { onCreate, onEdit, onDelete } = useBlockCommentsActions( reflowComments ); return ( <VStack className="editor-collab-sidebar-panel" style={ styles } role="tree" spacing="3" justify="flex-start" ref={ ( node ) => { // Sometimes previous sidebar unmounts after the new one mounts. // This ensures we always have the latest reference. if ( node ) { commentSidebarRef.current = node; } } } aria-label={ isFloating ? __( 'Unresolved notes' ) : __( 'All notes' ) } > <Comments threads={ comments } onEditComment={ onEdit } onAddReply={ onCreate } onCommentDelete={ onDelete } commentSidebarRef={ commentSidebarRef } reflowComments={ reflowComments } commentLastUpdated={ commentLastUpdated } isFloating={ isFloating } /> </VStack> ); } function NotesSidebar( { postId } ) { const { getActiveComplementaryArea } = useSelect( interfaceStore ); const { enableComplementaryArea } = useDispatch( interfaceStore ); const { toggleBlockSpotlight, selectBlock } = unlock( useDispatch( blockEditorStore ) ); const { selectNote } = unlock( useDispatch( editorStore ) ); const isLargeViewport = useViewportMatch( 'medium' ); const commentSidebarRef = useRef( null ); const { clientId, blockCommentId, isClassicBlock } = useSelect( ( select ) => { const { getBlockAttributes, getSelectedBlockClientId, getBlockName, } = select( blockEditorStore ); const _clientId = getSelectedBlockClientId(); return { clientId: _clientId, blockCommentId: _clientId ? getBlockAttributes( _clientId )?.metadata?.noteId : null, isClassicBlock: _clientId ? getBlockName( _clientId ) === 'core/freeform' : false, }; }, [] ); const { isDistractionFree } = useSelect( ( select ) => { const { get } = select( preferencesStore ); return { isDistractionFree: get( 'core', 'distractionFree' ), }; }, [] ); const selectedNote = useSelect( ( select ) => unlock( select( editorStore ) ).getSelectedNote(), [] ); const { resultComments, unresolvedSortedThreads, reflowComments, commentLastUpdated, } = useBlockComments( postId ); // Only enable the floating sidebar for large viewports. const showFloatingSidebar = isLargeViewport; // Fallback to "All notes" sidebar on smaller viewports. const showAllNotesSidebar = resultComments.length > 0 || ! showFloatingSidebar; useEnableFloatingSidebar( showFloatingSidebar && ( unresolvedSortedThreads.length > 0 || selectedNote !== undefined ) ); useShortcut( 'core/editor/new-note', ( event ) => { event.preventDefault(); openTheSidebar(); }, { // When multiple notes per block are supported. Remove note ID check. // See: https://github.com/WordPress/gutenberg/pull/75147. isDisabled: isDistractionFree || isClassicBlock || ! clientId || !! blockCommentId, } ); // Get the global styles to set the background color of the sidebar. const { merged: GlobalStyles } = useGlobalStylesContext(); const backgroundColor = GlobalStyles?.styles?.color?.background; // Find the current thread for the selected block. const currentThread = blockCommentId ? resultComments.find( ( thread ) => thread.id === blockCommentId ) : null; async function openTheSidebar( selectedClientId ) { const prevArea = await getActiveComplementaryArea( 'core' ); const activeNotesArea = SIDEBARS.find( ( name ) => name === prevArea ); const targetClientId = selectedClientId && selectedClientId !== clientId ? selectedClientId : clientId; const targetNote = resultComments.find( ( note ) => note.blockClientId === targetClientId ); if ( targetNote?.status === 'approved' ) { enableComplementaryArea( 'core', ALL_NOTES_SIDEBAR ); } else if ( ! activeNotesArea || ! showAllNotesSidebar ) { enableComplementaryArea( 'core', showFloatingSidebar ? FLOATING_NOTES_SIDEBAR : ALL_NOTES_SIDEBAR ); } const currentArea = await getActiveComplementaryArea( 'core' ); // Bail out if the current active area is not one of note sidebars. if ( ! SIDEBARS.includes( currentArea ) ) { return; } // A special case for the List View, where block selection isn't required to trigger an action. // The action won't do anything if the block is already selected. selectBlock( targetClientId, null ); toggleBlockSpotlight( targetClientId, true ); selectNote( targetNote ? targetNote.id : 'new', { focus: true } ); } if ( isDistractionFree ) { return <AddCommentMenuItem isDistractionFree />; } return ( <> { !! currentThread && ( <CommentAvatarIndicator thread={ currentThread } onClick={ openTheSidebar } /> ) } <AddCommentMenuItem onClick={ openTheSidebar } /> { showAllNotesSidebar && ( <PluginSidebar identifier={ ALL_NOTES_SIDEBAR } name={ ALL_NOTES_SIDEBAR } title={ __( 'All notes' ) } header={ <h2 className="interface-complementary-area-header__title"> { __( 'All notes' ) } </h2> } icon={ commentIcon } closeLabel={ __( 'Close Notes' ) } > <NotesSidebarContent comments={ resultComments } commentSidebarRef={ commentSidebarRef } /> </PluginSidebar> ) } { isLargeViewport && ( <PluginSidebar isPinnable={ false } header={ false } identifier={ FLOATING_NOTES_SIDEBAR } className="editor-collab-sidebar" headerClassName="editor-collab-sidebar__header" backgroundColor={ backgroundColor } > <NotesSidebarContent comments={ unresolvedSortedThreads } commentSidebarRef={ commentSidebarRef } reflowComments={ reflowComments } commentLastUpdated={ commentLastUpdated } styles={ { backgroundColor, } } isFloating /> </PluginSidebar> ) } </> ); } export default function NotesSidebarContainer() { const { postId, editorMode, revisionsMode } = useSelect( ( select ) => { const { getCurrentPostId, getEditorMode, isRevisionsMode } = unlock( select( editorStore ) ); return { postId: getCurrentPostId(), editorMode: getEditorMode(), revisionsMode: isRevisionsMode(), }; }, [] ); if ( ! postId || typeof postId !== 'number' ) { return null; } // Hide Notes sidebar for Code Editor and in-editor revision mode. if ( editorMode === 'text' || revisionsMode ) { return null; } return ( <PostTypeSupportCheck supportKeys="editor.notes"> <NotesSidebar postId={ postId } /> </PostTypeSupportCheck> ); }