UNPKG

@wordpress/block-library

Version:
615 lines (610 loc) 22.4 kB
/** * External dependencies */ import clsx from 'clsx'; /** * WordPress dependencies */ import { Placeholder, QueryControls, RadioControl, RangeControl, Spinner, ToggleControl, ToolbarGroup, __experimentalToggleGroupControl as ToggleGroupControl, __experimentalToggleGroupControlOptionIcon as ToggleGroupControlOptionIcon, __experimentalToolsPanel as ToolsPanel, __experimentalToolsPanelItem as ToolsPanelItem } from '@wordpress/components'; import { __, _x, sprintf } from '@wordpress/i18n'; import { dateI18n, format, getSettings } from '@wordpress/date'; import { InspectorControls, BlockControls, __experimentalImageSizeControl as ImageSizeControl, useBlockProps, store as blockEditorStore } from '@wordpress/block-editor'; import { useSelect, useDispatch } from '@wordpress/data'; import { pin, list, grid, alignNone, positionLeft, positionCenter, positionRight } from '@wordpress/icons'; import { store as coreStore } from '@wordpress/core-data'; import { store as noticeStore } from '@wordpress/notices'; import { useInstanceId } from '@wordpress/compose'; import { createInterpolateElement } from '@wordpress/element'; /** * Internal dependencies */ import { MIN_EXCERPT_LENGTH, MAX_EXCERPT_LENGTH, MAX_POSTS_COLUMNS, DEFAULT_EXCERPT_LENGTH } from './constants'; import { useToolsPanelDropdownMenuProps } from '../utils/hooks'; /** * Module Constants */ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; const CATEGORIES_LIST_QUERY = { per_page: -1, _fields: 'id,name', context: 'view' }; const USERS_LIST_QUERY = { per_page: -1, has_published_posts: ['post'], context: 'view' }; const imageAlignmentOptions = [{ value: 'none', icon: alignNone, label: __('None') }, { value: 'left', icon: positionLeft, label: __('Left') }, { value: 'center', icon: positionCenter, label: __('Center') }, { value: 'right', icon: positionRight, label: __('Right') }]; function getFeaturedImageDetails(post, size) { var _image$media_details$; const image = post._embedded?.['wp:featuredmedia']?.['0']; return { url: (_image$media_details$ = image?.media_details?.sizes?.[size]?.source_url) !== null && _image$media_details$ !== void 0 ? _image$media_details$ : image?.source_url, alt: image?.alt_text }; } function getCurrentAuthor(post) { return post._embedded?.author?.[0]; } function Controls({ attributes, setAttributes, postCount }) { var _categoriesList$reduc; const { postsToShow, order, orderBy, categories, selectedAuthor, displayFeaturedImage, displayPostContentRadio, displayPostContent, displayPostDate, displayAuthor, postLayout, columns, excerptLength, featuredImageAlign, featuredImageSizeSlug, featuredImageSizeWidth, featuredImageSizeHeight, addLinkToFeaturedImage } = attributes; const { imageSizes, defaultImageWidth, defaultImageHeight, categoriesList, authorList } = useSelect(select => { var _settings$imageDimens, _settings$imageDimens2; const { getEntityRecords, getUsers } = select(coreStore); const settings = select(blockEditorStore).getSettings(); return { defaultImageWidth: (_settings$imageDimens = settings.imageDimensions?.[featuredImageSizeSlug]?.width) !== null && _settings$imageDimens !== void 0 ? _settings$imageDimens : 0, defaultImageHeight: (_settings$imageDimens2 = settings.imageDimensions?.[featuredImageSizeSlug]?.height) !== null && _settings$imageDimens2 !== void 0 ? _settings$imageDimens2 : 0, imageSizes: settings.imageSizes, categoriesList: getEntityRecords('taxonomy', 'category', CATEGORIES_LIST_QUERY), authorList: getUsers(USERS_LIST_QUERY) }; }, [featuredImageSizeSlug]); const dropdownMenuProps = useToolsPanelDropdownMenuProps(); const imageSizeOptions = imageSizes.filter(({ slug }) => slug !== 'full').map(({ name, slug }) => ({ value: slug, label: name })); const categorySuggestions = (_categoriesList$reduc = categoriesList?.reduce((accumulator, category) => ({ ...accumulator, [category.name]: category }), {})) !== null && _categoriesList$reduc !== void 0 ? _categoriesList$reduc : {}; const selectCategories = tokens => { const hasNoSuggestion = tokens.some(token => typeof token === 'string' && !categorySuggestions[token]); if (hasNoSuggestion) { return; } // Categories that are already will be objects, while new additions will be strings (the name). // allCategories nomalizes the array so that they are all objects. const allCategories = tokens.map(token => { return typeof token === 'string' ? categorySuggestions[token] : token; }); // We do nothing if the category is not selected // from suggestions. if (allCategories.includes(null)) { return false; } setAttributes({ categories: allCategories }); }; return /*#__PURE__*/_jsxs(_Fragment, { children: [/*#__PURE__*/_jsxs(ToolsPanel, { label: __('Post content'), resetAll: () => setAttributes({ displayPostContent: false, displayPostContentRadio: 'excerpt', excerptLength: DEFAULT_EXCERPT_LENGTH }), dropdownMenuProps: dropdownMenuProps, children: [/*#__PURE__*/_jsx(ToolsPanelItem, { hasValue: () => !!displayPostContent, label: __('Display post content'), onDeselect: () => setAttributes({ displayPostContent: false }), isShownByDefault: true, children: /*#__PURE__*/_jsx(ToggleControl, { __nextHasNoMarginBottom: true, label: __('Display post content'), checked: displayPostContent, onChange: value => setAttributes({ displayPostContent: value }) }) }), displayPostContent && /*#__PURE__*/_jsx(ToolsPanelItem, { hasValue: () => displayPostContentRadio !== 'excerpt', label: __('Content length'), onDeselect: () => setAttributes({ displayPostContentRadio: 'excerpt' }), isShownByDefault: true, children: /*#__PURE__*/_jsx(RadioControl, { label: __('Content length'), selected: displayPostContentRadio, options: [{ label: __('Excerpt'), value: 'excerpt' }, { label: __('Full post'), value: 'full_post' }], onChange: value => setAttributes({ displayPostContentRadio: value }) }) }), displayPostContent && displayPostContentRadio === 'excerpt' && /*#__PURE__*/_jsx(ToolsPanelItem, { hasValue: () => excerptLength !== DEFAULT_EXCERPT_LENGTH, label: __('Max number of words'), onDeselect: () => setAttributes({ excerptLength: DEFAULT_EXCERPT_LENGTH }), isShownByDefault: true, children: /*#__PURE__*/_jsx(RangeControl, { __nextHasNoMarginBottom: true, __next40pxDefaultSize: true, label: __('Max number of words'), value: excerptLength, onChange: value => setAttributes({ excerptLength: value }), min: MIN_EXCERPT_LENGTH, max: MAX_EXCERPT_LENGTH }) })] }), /*#__PURE__*/_jsxs(ToolsPanel, { label: __('Post meta'), resetAll: () => setAttributes({ displayAuthor: false, displayPostDate: false }), dropdownMenuProps: dropdownMenuProps, children: [/*#__PURE__*/_jsx(ToolsPanelItem, { hasValue: () => !!displayAuthor, label: __('Display author name'), onDeselect: () => setAttributes({ displayAuthor: false }), isShownByDefault: true, children: /*#__PURE__*/_jsx(ToggleControl, { __nextHasNoMarginBottom: true, label: __('Display author name'), checked: displayAuthor, onChange: value => setAttributes({ displayAuthor: value }) }) }), /*#__PURE__*/_jsx(ToolsPanelItem, { hasValue: () => !!displayPostDate, label: __('Display post date'), onDeselect: () => setAttributes({ displayPostDate: false }), isShownByDefault: true, children: /*#__PURE__*/_jsx(ToggleControl, { __nextHasNoMarginBottom: true, label: __('Display post date'), checked: displayPostDate, onChange: value => setAttributes({ displayPostDate: value }) }) })] }), /*#__PURE__*/_jsxs(ToolsPanel, { label: __('Featured image'), resetAll: () => setAttributes({ displayFeaturedImage: false, featuredImageAlign: undefined, featuredImageSizeSlug: 'thumbnail', featuredImageSizeWidth: null, featuredImageSizeHeight: null, addLinkToFeaturedImage: false }), dropdownMenuProps: dropdownMenuProps, children: [/*#__PURE__*/_jsx(ToolsPanelItem, { hasValue: () => !!displayFeaturedImage, label: __('Display featured image'), onDeselect: () => setAttributes({ displayFeaturedImage: false }), isShownByDefault: true, children: /*#__PURE__*/_jsx(ToggleControl, { __nextHasNoMarginBottom: true, label: __('Display featured image'), checked: displayFeaturedImage, onChange: value => setAttributes({ displayFeaturedImage: value }) }) }), displayFeaturedImage && /*#__PURE__*/_jsxs(_Fragment, { children: [/*#__PURE__*/_jsx(ToolsPanelItem, { hasValue: () => featuredImageSizeSlug !== 'thumbnail' || featuredImageSizeWidth !== null || featuredImageSizeHeight !== null, label: __('Image size'), onDeselect: () => setAttributes({ featuredImageSizeSlug: 'thumbnail', featuredImageSizeWidth: null, featuredImageSizeHeight: null }), isShownByDefault: true, children: /*#__PURE__*/_jsx(ImageSizeControl, { onChange: value => { const newAttrs = {}; if (value.hasOwnProperty('width')) { newAttrs.featuredImageSizeWidth = value.width; } if (value.hasOwnProperty('height')) { newAttrs.featuredImageSizeHeight = value.height; } setAttributes(newAttrs); }, slug: featuredImageSizeSlug, width: featuredImageSizeWidth, height: featuredImageSizeHeight, imageWidth: defaultImageWidth, imageHeight: defaultImageHeight, imageSizeOptions: imageSizeOptions, imageSizeHelp: __('Select the size of the source image.'), onChangeImage: value => setAttributes({ featuredImageSizeSlug: value, featuredImageSizeWidth: undefined, featuredImageSizeHeight: undefined }) }) }), /*#__PURE__*/_jsx(ToolsPanelItem, { hasValue: () => !!featuredImageAlign, label: __('Image alignment'), onDeselect: () => setAttributes({ featuredImageAlign: undefined }), isShownByDefault: true, children: /*#__PURE__*/_jsx(ToggleGroupControl, { className: "editor-latest-posts-image-alignment-control", __nextHasNoMarginBottom: true, __next40pxDefaultSize: true, label: __('Image alignment'), value: featuredImageAlign || 'none', onChange: value => setAttributes({ featuredImageAlign: value !== 'none' ? value : undefined }), children: imageAlignmentOptions.map(({ value, icon, label }) => { return /*#__PURE__*/_jsx(ToggleGroupControlOptionIcon, { value: value, icon: icon, label: label }, value); }) }) }), /*#__PURE__*/_jsx(ToolsPanelItem, { hasValue: () => !!addLinkToFeaturedImage, label: __('Add link to featured image'), onDeselect: () => setAttributes({ addLinkToFeaturedImage: false }), isShownByDefault: true, children: /*#__PURE__*/_jsx(ToggleControl, { __nextHasNoMarginBottom: true, label: __('Add link to featured image'), checked: addLinkToFeaturedImage, onChange: value => setAttributes({ addLinkToFeaturedImage: value }) }) })] })] }), /*#__PURE__*/_jsxs(ToolsPanel, { label: __('Sorting and filtering'), resetAll: () => setAttributes({ order: 'desc', orderBy: 'date', postsToShow: 5, categories: undefined, selectedAuthor: undefined, columns: 3 }), dropdownMenuProps: dropdownMenuProps, children: [/*#__PURE__*/_jsx(ToolsPanelItem, { hasValue: () => order !== 'desc' || orderBy !== 'date' || postsToShow !== 5 || categories?.length > 0 || !!selectedAuthor, label: __('Sort and filter'), onDeselect: () => setAttributes({ order: 'desc', orderBy: 'date', postsToShow: 5, categories: undefined, selectedAuthor: undefined }), isShownByDefault: true, children: /*#__PURE__*/_jsx(QueryControls, { order, orderBy, numberOfItems: postsToShow, onOrderChange: value => setAttributes({ order: value }), onOrderByChange: value => setAttributes({ orderBy: value }), onNumberOfItemsChange: value => setAttributes({ postsToShow: value }), categorySuggestions: categorySuggestions, onCategoryChange: selectCategories, selectedCategories: categories, onAuthorChange: value => setAttributes({ selectedAuthor: '' !== value ? Number(value) : undefined }), authorList: authorList !== null && authorList !== void 0 ? authorList : [], selectedAuthorId: selectedAuthor }) }), postLayout === 'grid' && /*#__PURE__*/_jsx(ToolsPanelItem, { hasValue: () => columns !== 3, label: __('Columns'), onDeselect: () => setAttributes({ columns: 3 }), isShownByDefault: true, children: /*#__PURE__*/_jsx(RangeControl, { __nextHasNoMarginBottom: true, __next40pxDefaultSize: true, label: __('Columns'), value: columns, onChange: value => setAttributes({ columns: value }), min: 2, max: !postCount ? MAX_POSTS_COLUMNS : Math.min(MAX_POSTS_COLUMNS, postCount), required: true }) })] })] }); } export default function LatestPostsEdit({ attributes, setAttributes }) { var _latestPosts$length; const instanceId = useInstanceId(LatestPostsEdit); const { postsToShow, order, orderBy, categories, selectedAuthor, displayFeaturedImage, displayPostContentRadio, displayPostContent, displayPostDate, displayAuthor, postLayout, columns, excerptLength, featuredImageAlign, featuredImageSizeSlug, featuredImageSizeWidth, featuredImageSizeHeight, addLinkToFeaturedImage } = attributes; const { latestPosts } = useSelect(select => { const { getEntityRecords } = select(coreStore); const catIds = categories && categories.length > 0 ? categories.map(cat => cat.id) : []; const latestPostsQuery = Object.fromEntries(Object.entries({ categories: catIds, author: selectedAuthor, order, orderby: orderBy, per_page: postsToShow, _embed: 'author,wp:featuredmedia', ignore_sticky: true }).filter(([, value]) => typeof value !== 'undefined')); return { latestPosts: getEntityRecords('postType', 'post', latestPostsQuery) }; }, [postsToShow, order, orderBy, categories, selectedAuthor]); // If a user clicks to a link prevent redirection and show a warning. const { createWarningNotice } = useDispatch(noticeStore); const showRedirectionPreventedNotice = event => { event.preventDefault(); createWarningNotice(__('Links are disabled in the editor.'), { id: `block-library/core/latest-posts/redirection-prevented/${instanceId}`, type: 'snackbar' }); }; const hasPosts = !!latestPosts?.length; const inspectorControls = /*#__PURE__*/_jsx(InspectorControls, { children: /*#__PURE__*/_jsx(Controls, { attributes: attributes, setAttributes: setAttributes, postCount: (_latestPosts$length = latestPosts?.length) !== null && _latestPosts$length !== void 0 ? _latestPosts$length : 0 }) }); const blockProps = useBlockProps({ className: clsx({ 'wp-block-latest-posts__list': true, 'is-grid': postLayout === 'grid', 'has-dates': displayPostDate, 'has-author': displayAuthor, [`columns-${columns}`]: postLayout === 'grid' }) }); if (!hasPosts) { return /*#__PURE__*/_jsxs("div", { ...blockProps, children: [inspectorControls, /*#__PURE__*/_jsx(Placeholder, { icon: pin, label: __('Latest Posts'), children: !Array.isArray(latestPosts) ? /*#__PURE__*/_jsx(Spinner, {}) : __('No posts found.') })] }); } // Removing posts from display should be instant. const displayPosts = latestPosts.length > postsToShow ? latestPosts.slice(0, postsToShow) : latestPosts; const layoutControls = [{ icon: list, title: _x('List view', 'Latest posts block display setting'), onClick: () => setAttributes({ postLayout: 'list' }), isActive: postLayout === 'list' }, { icon: grid, title: _x('Grid view', 'Latest posts block display setting'), onClick: () => setAttributes({ postLayout: 'grid' }), isActive: postLayout === 'grid' }]; const dateFormat = getSettings().formats.date; return /*#__PURE__*/_jsxs(_Fragment, { children: [inspectorControls, /*#__PURE__*/_jsx(BlockControls, { children: /*#__PURE__*/_jsx(ToolbarGroup, { controls: layoutControls }) }), /*#__PURE__*/_jsx("ul", { ...blockProps, children: displayPosts.map(post => { const titleTrimmed = post.title.rendered.trim(); let excerpt = post.excerpt.rendered; const currentAuthor = getCurrentAuthor(post); const excerptElement = document.createElement('div'); excerptElement.innerHTML = excerpt; excerpt = excerptElement.textContent || excerptElement.innerText || ''; const { url: imageSourceUrl, alt: featuredImageAlt } = getFeaturedImageDetails(post, featuredImageSizeSlug); const imageClasses = clsx({ 'wp-block-latest-posts__featured-image': true, [`align${featuredImageAlign}`]: !!featuredImageAlign }); const renderFeaturedImage = displayFeaturedImage && imageSourceUrl; const featuredImage = renderFeaturedImage && /*#__PURE__*/_jsx("img", { src: imageSourceUrl, alt: featuredImageAlt, style: { maxWidth: featuredImageSizeWidth, maxHeight: featuredImageSizeHeight } }); const needsReadMore = excerptLength < excerpt.trim().split(' ').length && post.excerpt.raw === ''; const postExcerpt = needsReadMore ? /*#__PURE__*/_jsxs(_Fragment, { children: [excerpt.trim().split(' ', excerptLength).join(' '), createInterpolateElement(sprintf(/* translators: 1: Hidden accessibility text: Post title */ __('… <a>Read more<span>: %1$s</span></a>'), titleTrimmed || __('(no title)')), { a: /*#__PURE__*/ // eslint-disable-next-line jsx-a11y/anchor-has-content _jsx("a", { className: "wp-block-latest-posts__read-more", href: post.link, rel: "noopener noreferrer", onClick: showRedirectionPreventedNotice }), span: /*#__PURE__*/_jsx("span", { className: "screen-reader-text" }) })] }) : excerpt; return /*#__PURE__*/_jsxs("li", { children: [renderFeaturedImage && /*#__PURE__*/_jsx("div", { className: imageClasses, children: addLinkToFeaturedImage ? /*#__PURE__*/_jsx("a", { href: post.link, rel: "noreferrer noopener", onClick: showRedirectionPreventedNotice, children: featuredImage }) : featuredImage }), /*#__PURE__*/_jsx("a", { className: "wp-block-latest-posts__post-title", href: post.link, rel: "noreferrer noopener", dangerouslySetInnerHTML: !!titleTrimmed ? { __html: titleTrimmed } : undefined, onClick: showRedirectionPreventedNotice, children: !titleTrimmed ? __('(no title)') : null }), displayAuthor && currentAuthor && /*#__PURE__*/_jsx("div", { className: "wp-block-latest-posts__post-author", children: sprintf(/* translators: byline. %s: author. */ __('by %s'), currentAuthor.name) }), displayPostDate && post.date_gmt && /*#__PURE__*/_jsx("time", { dateTime: format('c', post.date_gmt), className: "wp-block-latest-posts__post-date", children: dateI18n(dateFormat, post.date_gmt) }), displayPostContent && displayPostContentRadio === 'excerpt' && /*#__PURE__*/_jsx("div", { className: "wp-block-latest-posts__post-excerpt", children: postExcerpt }), displayPostContent && displayPostContentRadio === 'full_post' && /*#__PURE__*/_jsx("div", { className: "wp-block-latest-posts__post-full-content", dangerouslySetInnerHTML: { __html: post.content.raw.trim() } })] }, post.id); }) })] }); } //# sourceMappingURL=edit.js.map