UNPKG

@wordpress/editor

Version:
256 lines (243 loc) 7.22 kB
/** * WordPress dependencies */ import { __ } from '@wordpress/i18n'; import { SelectControl, Dropdown, Button, Notice } from '@wordpress/components'; import { useSelect, useDispatch } from '@wordpress/data'; import { store as coreStore } from '@wordpress/core-data'; import { __experimentalInspectorPopoverHeader as InspectorPopoverHeader } from '@wordpress/block-editor'; import { useState, useMemo } from '@wordpress/element'; import { addTemplate } from '@wordpress/icons'; import { store as noticesStore } from '@wordpress/notices'; /** * Internal dependencies */ import { store as editorStore } from '../../store'; import CreateNewTemplateModal from './create-new-template-modal'; import { useAllowSwitchingTemplates } from './hooks'; import PostPanelRow from '../post-panel-row'; function PostTemplateToggle( { isOpen, onClick } ) { const templateTitle = useSelect( ( select ) => { const templateSlug = select( editorStore ).getEditedPostAttribute( 'template' ); const { supportsTemplateMode, availableTemplates } = select( editorStore ).getEditorSettings(); if ( ! supportsTemplateMode && availableTemplates[ templateSlug ] ) { return availableTemplates[ templateSlug ]; } const template = select( coreStore ).canUser( 'create', { kind: 'postType', name: 'wp_template', } ) && select( editorStore ).getCurrentTemplateId(); return ( template?.title || template?.slug || availableTemplates?.[ templateSlug ] ); }, [] ); return ( <Button __next40pxDefaultSize variant="tertiary" aria-expanded={ isOpen } aria-label={ __( 'Template options' ) } onClick={ onClick } > { templateTitle ?? __( 'Default template' ) } </Button> ); } /** * Renders the dropdown content for selecting a post template. * * @param {Object} props The component props. * @param {Function} props.onClose The function to close the dropdown. * * @return {React.ReactNode} The rendered dropdown content. */ function PostTemplateDropdownContent( { onClose } ) { const allowSwitchingTemplate = useAllowSwitchingTemplates(); const { availableTemplates, fetchedTemplates, selectedTemplateSlug, canCreate, canEdit, currentTemplateId, onNavigateToEntityRecord, getEditorSettings, } = useSelect( ( select ) => { const { canUser, getEntityRecords } = select( coreStore ); const editorSettings = select( editorStore ).getEditorSettings(); const canCreateTemplates = canUser( 'create', { kind: 'postType', name: 'wp_template', } ); const _currentTemplateId = select( editorStore ).getCurrentTemplateId(); return { availableTemplates: editorSettings.availableTemplates, fetchedTemplates: canCreateTemplates ? getEntityRecords( 'postType', 'wp_template', { post_type: select( editorStore ).getCurrentPostType(), per_page: -1, } ) : undefined, selectedTemplateSlug: select( editorStore ).getEditedPostAttribute( 'template' ), canCreate: allowSwitchingTemplate && canCreateTemplates && editorSettings.supportsTemplateMode, canEdit: allowSwitchingTemplate && canCreateTemplates && editorSettings.supportsTemplateMode && !! _currentTemplateId, currentTemplateId: _currentTemplateId, onNavigateToEntityRecord: editorSettings.onNavigateToEntityRecord, getEditorSettings: select( editorStore ).getEditorSettings, }; }, [ allowSwitchingTemplate ] ); const options = useMemo( () => Object.entries( { ...availableTemplates, ...Object.fromEntries( ( fetchedTemplates ?? [] ).map( ( { slug, title } ) => [ slug, title.rendered, ] ) ), } ).map( ( [ slug, title ] ) => ( { value: slug, label: title } ) ), [ availableTemplates, fetchedTemplates ] ); const selectedOption = options.find( ( option ) => option.value === selectedTemplateSlug ) ?? options.find( ( option ) => ! option.value ); // The default option has '' value. const { editPost } = useDispatch( editorStore ); const { createSuccessNotice } = useDispatch( noticesStore ); const [ isCreateModalOpen, setIsCreateModalOpen ] = useState( false ); return ( <div className="editor-post-template__classic-theme-dropdown"> <InspectorPopoverHeader title={ __( 'Template' ) } help={ __( 'Templates define the way content is displayed when viewing your site.' ) } actions={ canCreate ? [ { icon: addTemplate, label: __( 'Add template' ), onClick: () => setIsCreateModalOpen( true ), }, ] : [] } onClose={ onClose } /> { ! allowSwitchingTemplate ? ( <Notice status="warning" isDismissible={ false }> { __( 'The posts page template cannot be changed.' ) } </Notice> ) : ( <SelectControl __next40pxDefaultSize hideLabelFromVision label={ __( 'Template' ) } value={ selectedOption?.value ?? '' } options={ options } onChange={ ( slug ) => editPost( { template: slug || '' } ) } /> ) } { canEdit && onNavigateToEntityRecord && ( <p> <Button __next40pxDefaultSize variant="link" onClick={ () => { onNavigateToEntityRecord( { postId: currentTemplateId, postType: 'wp_template', } ); onClose(); createSuccessNotice( __( 'Editing template. Changes made here affect all posts and pages that use the template.' ), { type: 'snackbar', actions: [ { label: __( 'Go back' ), onClick: () => getEditorSettings().onNavigateToPreviousEntityRecord(), }, ], } ); } } > { __( 'Edit template' ) } </Button> </p> ) } { isCreateModalOpen && ( <CreateNewTemplateModal onClose={ () => setIsCreateModalOpen( false ) } /> ) } </div> ); } function ClassicThemeControl() { const [ popoverAnchor, setPopoverAnchor ] = useState( null ); // Memoize popoverProps to avoid returning a new object every time. const popoverProps = useMemo( () => ( { // Anchor the popover to the middle of the entire row so that it doesn't // move around when the label changes. anchor: popoverAnchor, className: 'editor-post-template__dropdown', placement: 'left-start', offset: 36, shift: true, } ), [ popoverAnchor ] ); return ( <PostPanelRow label={ __( 'Template' ) } ref={ setPopoverAnchor }> <Dropdown popoverProps={ popoverProps } focusOnMount renderToggle={ ( { isOpen, onToggle } ) => ( <PostTemplateToggle isOpen={ isOpen } onClick={ onToggle } /> ) } renderContent={ ( { onClose } ) => ( <PostTemplateDropdownContent onClose={ onClose } /> ) } /> </PostPanelRow> ); } /** * Provides a dropdown menu for selecting and managing post templates. * * The dropdown menu includes a button for toggling the menu, a list of available templates, and options for creating and editing templates. * * @return {React.ReactNode} The rendered ClassicThemeControl component. */ export default ClassicThemeControl;