UNPKG

@wordpress/block-library

Version:
266 lines (252 loc) 6.96 kB
/** * External dependencies */ import clsx from 'clsx'; /** * WordPress dependencies */ import { useEntityProp, store as coreStore } from '@wordpress/core-data'; import { useMemo, useState } from '@wordpress/element'; import { dateI18n, humanTimeDiff, getSettings as getDateSettings, } from '@wordpress/date'; import { AlignmentControl, BlockControls, InspectorControls, useBlockProps, __experimentalDateFormatPicker as DateFormatPicker, __experimentalPublishDateTimePicker as PublishDateTimePicker, } from '@wordpress/block-editor'; import { Dropdown, ToolbarGroup, ToolbarButton, ToggleControl, __experimentalToolsPanel as ToolsPanel, __experimentalToolsPanelItem as ToolsPanelItem, } from '@wordpress/components'; import { __, _x, sprintf } from '@wordpress/i18n'; import { edit } from '@wordpress/icons'; import { DOWN } from '@wordpress/keycodes'; import { useSelect } from '@wordpress/data'; /** * Internal dependencies */ import { useToolsPanelDropdownMenuProps } from '../utils/hooks'; export default function PostDateEdit( { attributes: { textAlign, format, isLink, displayType }, context: { postId, postType: postTypeSlug, queryId }, setAttributes, } ) { const blockProps = useBlockProps( { className: clsx( { [ `has-text-align-${ textAlign }` ]: textAlign, [ `wp-block-post-date__modified-date` ]: displayType === 'modified', } ), } ); const dropdownMenuProps = useToolsPanelDropdownMenuProps(); // 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: popoverAnchor } ), [ popoverAnchor ] ); const isDescendentOfQueryLoop = Number.isFinite( queryId ); const dateSettings = getDateSettings(); const [ siteFormat = dateSettings.formats.date ] = useEntityProp( 'root', 'site', 'date_format' ); const [ siteTimeFormat = dateSettings.formats.time ] = useEntityProp( 'root', 'site', 'time_format' ); const [ date, setDate ] = useEntityProp( 'postType', postTypeSlug, displayType, postId ); const postType = useSelect( ( select ) => postTypeSlug ? select( coreStore ).getPostType( postTypeSlug ) : null, [ postTypeSlug ] ); const dateLabel = displayType === 'date' ? __( 'Post Date' ) : __( 'Post Modified Date' ); let postDate = date ? ( <time dateTime={ dateI18n( 'c', date ) } ref={ setPopoverAnchor }> { format === 'human-diff' ? humanTimeDiff( date ) : dateI18n( format || siteFormat, date ) } </time> ) : ( dateLabel ); if ( isLink && date ) { postDate = ( <a href="#post-date-pseudo-link" onClick={ ( event ) => event.preventDefault() } > { postDate } </a> ); } return ( <> <BlockControls group="block"> <AlignmentControl value={ textAlign } onChange={ ( nextAlign ) => { setAttributes( { textAlign: nextAlign } ); } } /> { date && displayType === 'date' && ! isDescendentOfQueryLoop && ( <ToolbarGroup> <Dropdown popoverProps={ popoverProps } renderContent={ ( { onClose } ) => ( <PublishDateTimePicker currentDate={ date } onChange={ setDate } is12Hour={ is12HourFormat( siteTimeFormat ) } onClose={ onClose } dateOrder={ /* translators: Order of day, month, and year. Available formats are 'dmy', 'mdy', and 'ymd'. */ _x( 'dmy', 'date order' ) } /> ) } renderToggle={ ( { isOpen, onToggle } ) => { const openOnArrowDown = ( event ) => { if ( ! isOpen && event.keyCode === DOWN ) { event.preventDefault(); onToggle(); } }; return ( <ToolbarButton aria-expanded={ isOpen } icon={ edit } title={ __( 'Change Date' ) } onClick={ onToggle } onKeyDown={ openOnArrowDown } /> ); } } /> </ToolbarGroup> ) } </BlockControls> <InspectorControls> <ToolsPanel label={ __( 'Settings' ) } resetAll={ () => { setAttributes( { format: undefined, isLink: false, displayType: 'date', } ); } } dropdownMenuProps={ dropdownMenuProps } > <ToolsPanelItem hasValue={ () => !! format } label={ __( 'Date Format' ) } onDeselect={ () => setAttributes( { format: undefined } ) } isShownByDefault > <DateFormatPicker format={ format } defaultFormat={ siteFormat } onChange={ ( nextFormat ) => setAttributes( { format: nextFormat } ) } /> </ToolsPanelItem> <ToolsPanelItem hasValue={ () => isLink !== false } label={ postType?.labels.singular_name ? sprintf( // translators: %s: Name of the post type e.g: "post". __( 'Link to %s' ), postType.labels.singular_name.toLowerCase() ) : __( 'Link to post' ) } onDeselect={ () => setAttributes( { isLink: false } ) } isShownByDefault > <ToggleControl __nextHasNoMarginBottom label={ postType?.labels.singular_name ? sprintf( // translators: %s: Name of the post type e.g: "post". __( 'Link to %s' ), postType.labels.singular_name.toLowerCase() ) : __( 'Link to post' ) } onChange={ () => setAttributes( { isLink: ! isLink } ) } checked={ isLink } /> </ToolsPanelItem> <ToolsPanelItem hasValue={ () => displayType !== 'date' } label={ __( 'Display last modified date' ) } onDeselect={ () => setAttributes( { displayType: 'date' } ) } isShownByDefault > <ToggleControl __nextHasNoMarginBottom label={ __( 'Display last modified date' ) } onChange={ ( value ) => setAttributes( { displayType: value ? 'modified' : 'date', } ) } checked={ displayType === 'modified' } help={ __( 'Only shows if the post has been modified' ) } /> </ToolsPanelItem> </ToolsPanel> </InspectorControls> <div { ...blockProps }>{ postDate }</div> </> ); } export function is12HourFormat( format ) { // To know if the time format is a 12 hour time, look for any of the 12 hour // format characters: 'a', 'A', 'g', and 'h'. The character must be // unescaped, i.e. not preceded by a '\'. Coincidentally, 'aAgh' is how I // feel when working with regular expressions. // https://www.php.net/manual/en/datetime.format.php return /(?:^|[^\\])[aAgh]/.test( format ); }