diff --git a/packages/editor/src/components/blog-title/index.js b/packages/editor/src/components/blog-title/index.js new file mode 100644 index 00000000000000..a82498b5220f4d --- /dev/null +++ b/packages/editor/src/components/blog-title/index.js @@ -0,0 +1,124 @@ +/** + * WordPress dependencies + */ +import { __, sprintf } from '@wordpress/i18n'; +import { debounce } from '@wordpress/compose'; +import { useSelect, useDispatch } from '@wordpress/data'; +import { store as coreStore } from '@wordpress/core-data'; +import { decodeEntities } from '@wordpress/html-entities'; +import { + Button, + Dropdown, + __experimentalInputControl as InputControl, +} from '@wordpress/components'; +import { useState, useMemo } from '@wordpress/element'; +import { __experimentalInspectorPopoverHeader as InspectorPopoverHeader } from '@wordpress/block-editor'; + +/** + * Internal dependencies + */ +import { TEMPLATE_POST_TYPE } from '../../store/constants'; +import PostPanelRow from '../post-panel-row'; +import { store as editorStore } from '../../store'; + +const EMPTY_OBJECT = {}; + +export default function BlogTitle() { + const { editEntityRecord } = useDispatch( coreStore ); + const { postsPageTitle, postsPageId, isTemplate, postSlug } = useSelect( + ( select ) => { + const { getEntityRecord, getEditedEntityRecord } = + select( coreStore ); + const siteSettings = getEntityRecord( 'root', 'site' ); + const _postsPageRecord = siteSettings?.page_for_posts + ? getEditedEntityRecord( + 'postType', + 'page', + siteSettings?.page_for_posts + ) + : EMPTY_OBJECT; + const { getEditedPostAttribute, getCurrentPostType } = + select( editorStore ); + return { + postsPageId: _postsPageRecord?.id, + postsPageTitle: _postsPageRecord?.title, + isTemplate: getCurrentPostType() === TEMPLATE_POST_TYPE, + postSlug: getEditedPostAttribute( 'slug' ), + }; + }, + [] + ); + // Use internal state instead of a ref to make sure that the component + // re-renders when the popover's anchor updates. + 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, + placement: 'left-start', + offset: 36, + shift: true, + } ), + [ popoverAnchor ] + ); + + if ( + ! isTemplate || + ! [ 'home', 'index' ].includes( postSlug ) || + ! postsPageId + ) { + return null; + } + + const setPostsPageTitle = ( newValue ) => { + editEntityRecord( 'postType', 'page', postsPageId, { + title: newValue, + } ); + }; + const decodedTitle = decodeEntities( postsPageTitle ); + return ( + + ( + + ) } + renderContent={ ( { onClose } ) => ( + <> + + + + ) } + /> + + ); +} diff --git a/packages/editor/src/components/blog-title/style.scss b/packages/editor/src/components/blog-title/style.scss new file mode 100644 index 00000000000000..164e82979e0b30 --- /dev/null +++ b/packages/editor/src/components/blog-title/style.scss @@ -0,0 +1,4 @@ +.editor-blog-title-dropdown__content .components-popover__content { + min-width: 320px; + padding: $grid-unit-20; +} diff --git a/packages/editor/src/components/posts-per-page/index.js b/packages/editor/src/components/posts-per-page/index.js new file mode 100644 index 00000000000000..625aeba7bd824f --- /dev/null +++ b/packages/editor/src/components/posts-per-page/index.js @@ -0,0 +1,101 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { useSelect, useDispatch } from '@wordpress/data'; +import { store as coreStore } from '@wordpress/core-data'; +import { + Button, + Dropdown, + __experimentalNumberControl as NumberControl, +} from '@wordpress/components'; +import { useState, useMemo } from '@wordpress/element'; +import { __experimentalInspectorPopoverHeader as InspectorPopoverHeader } from '@wordpress/block-editor'; + +/** + * Internal dependencies + */ +import { TEMPLATE_POST_TYPE } from '../../store/constants'; +import { store as editorStore } from '../../store'; +import PostPanelRow from '../post-panel-row'; + +export default function PostsPerPage() { + const { editEntityRecord } = useDispatch( coreStore ); + const { postsPerPage, isTemplate, postSlug } = useSelect( ( select ) => { + const { getEditedPostAttribute, getCurrentPostType } = + select( editorStore ); + const { getEditedEntityRecord } = select( coreStore ); + const siteSettings = getEditedEntityRecord( 'root', 'site' ); + return { + isTemplate: getCurrentPostType() === TEMPLATE_POST_TYPE, + postSlug: getEditedPostAttribute( 'slug' ), + postsPerPage: siteSettings?.posts_per_page || 1, + }; + }, [] ); + // Use internal state instead of a ref to make sure that the component + // re-renders when the popover's anchor updates. + 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, + placement: 'left-start', + offset: 36, + shift: true, + } ), + [ popoverAnchor ] + ); + + if ( ! isTemplate || ! [ 'home', 'index' ].includes( postSlug ) ) { + return null; + } + const setPostsPerPage = ( newValue ) => { + editEntityRecord( 'root', 'site', undefined, { + posts_per_page: newValue, + } ); + }; + return ( + + ( + + ) } + renderContent={ ( { onClose } ) => ( + <> + + + + ) } + /> + + ); +} diff --git a/packages/editor/src/components/posts-per-page/style.scss b/packages/editor/src/components/posts-per-page/style.scss new file mode 100644 index 00000000000000..e77e9b33206320 --- /dev/null +++ b/packages/editor/src/components/posts-per-page/style.scss @@ -0,0 +1,4 @@ +.editor-posts-per-page-dropdown__content .components-popover__content { + min-width: 320px; + padding: $grid-unit-20; +} diff --git a/packages/editor/src/components/sidebar/post-summary.js b/packages/editor/src/components/sidebar/post-summary.js index 839d60c3b188a2..8f1fb23092a5bf 100644 --- a/packages/editor/src/components/sidebar/post-summary.js +++ b/packages/editor/src/components/sidebar/post-summary.js @@ -25,6 +25,9 @@ import PostSyncStatus from '../post-sync-status'; import PostTemplatePanel from '../post-template/panel'; import PostTrashPanel from '../post-trash/panel'; import PostURLPanel from '../post-url/panel'; +import BlogTitle from '../blog-title'; +import PostsPerPage from '../posts-per-page'; +import SiteDiscussion from '../site-discussion'; import { store as editorStore } from '../../store'; import { NAVIGATION_POST_TYPE, @@ -84,10 +87,13 @@ export default function PostSummary( { onActionPerformed } ) { + + + - { isTemplate && } + { fills } { ! isPattern && ! isTemplate && diff --git a/packages/editor/src/components/site-discussion/index.js b/packages/editor/src/components/site-discussion/index.js new file mode 100644 index 00000000000000..e4bd60db3f8a7c --- /dev/null +++ b/packages/editor/src/components/site-discussion/index.js @@ -0,0 +1,138 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { useSelect, useDispatch } from '@wordpress/data'; +import { store as coreStore } from '@wordpress/core-data'; +import { + Button, + Dropdown, + RadioControl, + __experimentalVStack as VStack, + __experimentalText as Text, +} from '@wordpress/components'; +import { useState, useMemo } from '@wordpress/element'; +import { __experimentalInspectorPopoverHeader as InspectorPopoverHeader } from '@wordpress/block-editor'; + +/** + * Internal dependencies + */ +import { TEMPLATE_POST_TYPE } from '../../store/constants'; +import PostPanelRow from '../post-panel-row'; +import { store as editorStore } from '../../store'; + +const COMMENT_OPTIONS = [ + { + label: ( + <> + { __( 'Open' ) } + + { __( 'Visitors can add new comments and replies.' ) } + + + ), + value: 'open', + }, + { + label: ( + <> + { __( 'Closed' ) } + + { __( 'Visitors cannot add new comments or replies.' ) } + + + { __( 'Existing comments remain visible.' ) } + + + ), + value: '', + }, +]; + +export default function SiteDiscussion() { + const { editEntityRecord } = useDispatch( coreStore ); + const { allowCommentsOnNewPosts, isTemplate, postSlug } = useSelect( + ( select ) => { + const { getEditedPostAttribute, getCurrentPostType } = + select( editorStore ); + const { getEditedEntityRecord } = select( coreStore ); + const siteSettings = getEditedEntityRecord( 'root', 'site' ); + return { + isTemplate: getCurrentPostType() === TEMPLATE_POST_TYPE, + postSlug: getEditedPostAttribute( 'slug' ), + allowCommentsOnNewPosts: + siteSettings?.default_comment_status || '', + }; + }, + [] + ); + // Use internal state instead of a ref to make sure that the component + // re-renders when the popover's anchor updates. + 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, + placement: 'left-start', + offset: 36, + shift: true, + } ), + [ popoverAnchor ] + ); + + if ( ! isTemplate || ! [ 'home', 'index' ].includes( postSlug ) ) { + return null; + } + const setAllowCommentsOnNewPosts = ( newValue ) => { + editEntityRecord( 'root', 'site', undefined, { + default_comment_status: newValue ? 'open' : null, + } ); + }; + return ( + + ( + + ) } + renderContent={ ( { onClose } ) => ( + <> + + + + { __( + 'Changes will apply to new posts only. Individual posts may override these settings.' + ) } + + + + + ) } + /> + + ); +} diff --git a/packages/editor/src/components/site-discussion/style.scss b/packages/editor/src/components/site-discussion/style.scss new file mode 100644 index 00000000000000..2c54424207ea5c --- /dev/null +++ b/packages/editor/src/components/site-discussion/style.scss @@ -0,0 +1,19 @@ +.editor-site-discussion-dropdown__content .components-popover__content { + min-width: 320px; + padding: $grid-unit-20; +} + +.editor-site-discussion__options { + // TODO: it's not great to override component styles.. This might be resolved + // by the new radio control component. + .components-radio-control__option { + align-items: flex-start; + } + + label { + .components-text { + display: block; + margin-top: $grid-unit-05; + } + } +} diff --git a/packages/editor/src/components/template-areas/index.js b/packages/editor/src/components/template-areas/index.js index 25cb59bf9b2ed5..0d3bbe42d02eed 100644 --- a/packages/editor/src/components/template-areas/index.js +++ b/packages/editor/src/components/template-areas/index.js @@ -15,6 +15,7 @@ import { __ } from '@wordpress/i18n'; */ import { store as editorStore } from '../../store'; import { unlock } from '../../lock-unlock'; +import { TEMPLATE_POST_TYPE } from '../../store/constants'; function TemplateAreaItem( { area, clientId } ) { const { selectBlock, toggleBlockHighlight } = @@ -54,13 +55,21 @@ function TemplateAreaItem( { area, clientId } ) { } export default function TemplateAreas() { - const templateParts = useSelect( - ( select ) => - unlock( select( editorStore ) ).getCurrentTemplateTemplateParts(), - [] - ); + const { isTemplate, templateParts } = useSelect( ( select ) => { + const _isTemplate = + select( editorStore ).getCurrentPostType() === TEMPLATE_POST_TYPE; + + return { + isTemplate: _isTemplate, + templateParts: + _isTemplate && + unlock( + select( editorStore ) + ).getCurrentTemplateTemplateParts(), + }; + }, [] ); - if ( ! templateParts.length ) { + if ( ! isTemplate || ! templateParts.length ) { return null; } diff --git a/packages/editor/src/style.scss b/packages/editor/src/style.scss index a28a2db3ae4a28..793d83e54c5488 100644 --- a/packages/editor/src/style.scss +++ b/packages/editor/src/style.scss @@ -4,6 +4,7 @@ @import "./components/block-manager/style.scss"; @import "./components/collapsible-block-toolbar/style.scss"; @import "./components/block-settings-menu/style.scss"; +@import "./components/blog-title/style.scss"; @import "./components/document-bar/style.scss"; @import "./components/document-outline/style.scss"; @import "./components/document-tools/style.scss"; @@ -39,11 +40,13 @@ @import "./components/post-title/style.scss"; @import "./components/post-url/style.scss"; @import "./components/post-visibility/style.scss"; +@import "./components/posts-per-page/style.scss"; @import "./components/post-trash/style.scss"; @import "./components/preview-dropdown/style.scss"; @import "./components/save-publish-panels/style.scss"; @import "./components/start-page-options/style.scss"; @import "./components/start-template-options/style.scss"; @import "./components/sidebar/style.scss"; +@import "./components/site-discussion/style.scss"; @import "./components/table-of-contents/style.scss"; @import "./components/template-areas/style.scss";