@wordpress/block-library
Version:
Block library for the WordPress editor.
394 lines (369 loc) • 14.1 kB
JavaScript
import { createElement, Fragment } from "@wordpress/element";
/**
* External dependencies
*/
import { get, includes, pickBy } from 'lodash';
import classnames from 'classnames';
/**
* WordPress dependencies
*/
import { BaseControl, PanelBody, Placeholder, QueryControls, RadioControl, RangeControl, Spinner, ToggleControl, ToolbarGroup } from '@wordpress/components';
import { __, sprintf } from '@wordpress/i18n';
import { dateI18n, format, getSettings } from '@wordpress/date';
import { InspectorControls, BlockAlignmentToolbar, BlockControls, __experimentalImageSizeControl as ImageSizeControl, useBlockProps, store as blockEditorStore } from '@wordpress/block-editor';
import { useSelect, useDispatch } from '@wordpress/data';
import { pin, list, grid } from '@wordpress/icons';
import { store as coreStore } from '@wordpress/core-data';
import { store as noticeStore } from '@wordpress/notices';
import { useInstanceId } from '@wordpress/compose';
/**
* Internal dependencies
*/
import { MIN_EXCERPT_LENGTH, MAX_EXCERPT_LENGTH, MAX_POSTS_COLUMNS } from './constants';
/**
* Module Constants
*/
const CATEGORIES_LIST_QUERY = {
per_page: -1,
context: 'view'
};
const USERS_LIST_QUERY = {
per_page: -1,
has_published_posts: ['post'],
context: 'view'
};
function getFeaturedImageDetails(post, size) {
var _image$media_details$, _image$media_details, _image$media_details$2, _image$media_details$3;
const image = get(post, ['_embedded', 'wp:featuredmedia', '0']);
return {
url: (_image$media_details$ = image === null || image === void 0 ? void 0 : (_image$media_details = image.media_details) === null || _image$media_details === void 0 ? void 0 : (_image$media_details$2 = _image$media_details.sizes) === null || _image$media_details$2 === void 0 ? void 0 : (_image$media_details$3 = _image$media_details$2[size]) === null || _image$media_details$3 === void 0 ? void 0 : _image$media_details$3.source_url) !== null && _image$media_details$ !== void 0 ? _image$media_details$ : image === null || image === void 0 ? void 0 : image.source_url,
alt: image === null || image === void 0 ? void 0 : image.alt_text
};
}
export default function LatestPostsEdit(_ref) {
var _categoriesList$reduc;
let {
attributes,
setAttributes
} = _ref;
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 {
imageSizes,
latestPosts,
defaultImageWidth,
defaultImageHeight,
categoriesList,
authorList
} = useSelect(select => {
const {
getEntityRecords,
getUsers
} = select(coreStore);
const settings = select(blockEditorStore).getSettings();
const catIds = categories && categories.length > 0 ? categories.map(cat => cat.id) : [];
const latestPostsQuery = pickBy({
categories: catIds,
author: selectedAuthor,
order,
orderby: orderBy,
per_page: postsToShow,
_embed: 'wp:featuredmedia'
}, value => typeof value !== 'undefined');
return {
defaultImageWidth: get(settings.imageDimensions, [featuredImageSizeSlug, 'width'], 0),
defaultImageHeight: get(settings.imageDimensions, [featuredImageSizeSlug, 'height'], 0),
imageSizes: settings.imageSizes,
latestPosts: getEntityRecords('postType', 'post', latestPostsQuery),
categoriesList: getEntityRecords('taxonomy', 'category', CATEGORIES_LIST_QUERY),
authorList: getUsers(USERS_LIST_QUERY)
};
}, [featuredImageSizeSlug, postsToShow, order, orderBy, categories, selectedAuthor]); // If a user clicks to a link prevent redirection and show a warning.
const {
createWarningNotice,
removeNotice
} = useDispatch(noticeStore);
let noticeId;
const showRedirectionPreventedNotice = event => {
event.preventDefault(); // Remove previous warning if any, to show one at a time per block.
removeNotice(noticeId);
noticeId = `block-library/core/latest-posts/redirection-prevented/${instanceId}`;
createWarningNotice(__('Links are disabled in the editor.'), {
id: noticeId,
type: 'snackbar'
});
};
const imageSizeOptions = imageSizes.filter(_ref2 => {
let {
slug
} = _ref2;
return slug !== 'full';
}).map(_ref3 => {
let {
name,
slug
} = _ref3;
return {
value: slug,
label: name
};
});
const categorySuggestions = (_categoriesList$reduc = categoriesList === null || categoriesList === void 0 ? void 0 : 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 (includes(allCategories, null)) {
return false;
}
setAttributes({
categories: allCategories
});
};
const hasPosts = !!(latestPosts !== null && latestPosts !== void 0 && latestPosts.length);
const inspectorControls = createElement(InspectorControls, null, createElement(PanelBody, {
title: __('Post content settings')
}, createElement(ToggleControl, {
label: __('Post content'),
checked: displayPostContent,
onChange: value => setAttributes({
displayPostContent: value
})
}), displayPostContent && createElement(RadioControl, {
label: __('Show:'),
selected: displayPostContentRadio,
options: [{
label: __('Excerpt'),
value: 'excerpt'
}, {
label: __('Full post'),
value: 'full_post'
}],
onChange: value => setAttributes({
displayPostContentRadio: value
})
}), displayPostContent && displayPostContentRadio === 'excerpt' && createElement(RangeControl, {
label: __('Max number of words in excerpt'),
value: excerptLength,
onChange: value => setAttributes({
excerptLength: value
}),
min: MIN_EXCERPT_LENGTH,
max: MAX_EXCERPT_LENGTH
})), createElement(PanelBody, {
title: __('Post meta settings')
}, createElement(ToggleControl, {
label: __('Display author name'),
checked: displayAuthor,
onChange: value => setAttributes({
displayAuthor: value
})
}), createElement(ToggleControl, {
label: __('Display post date'),
checked: displayPostDate,
onChange: value => setAttributes({
displayPostDate: value
})
})), createElement(PanelBody, {
title: __('Featured image settings')
}, createElement(ToggleControl, {
label: __('Display featured image'),
checked: displayFeaturedImage,
onChange: value => setAttributes({
displayFeaturedImage: value
})
}), displayFeaturedImage && createElement(Fragment, null, createElement(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,
onChangeImage: value => setAttributes({
featuredImageSizeSlug: value,
featuredImageSizeWidth: undefined,
featuredImageSizeHeight: undefined
})
}), createElement(BaseControl, {
className: "editor-latest-posts-image-alignment-control"
}, createElement(BaseControl.VisualLabel, null, __('Image alignment')), createElement(BlockAlignmentToolbar, {
value: featuredImageAlign,
onChange: value => setAttributes({
featuredImageAlign: value
}),
controls: ['left', 'center', 'right'],
isCollapsed: false
})), createElement(ToggleControl, {
label: __('Add link to featured image'),
checked: addLinkToFeaturedImage,
onChange: value => setAttributes({
addLinkToFeaturedImage: value
})
}))), createElement(PanelBody, {
title: __('Sorting and filtering')
}, createElement(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' && createElement(RangeControl, {
label: __('Columns'),
value: columns,
onChange: value => setAttributes({
columns: value
}),
min: 2,
max: !hasPosts ? MAX_POSTS_COLUMNS : Math.min(MAX_POSTS_COLUMNS, latestPosts.length),
required: true
})));
const blockProps = useBlockProps({
className: classnames({
'wp-block-latest-posts__list': true,
'is-grid': postLayout === 'grid',
'has-dates': displayPostDate,
'has-author': displayAuthor,
[`columns-${columns}`]: postLayout === 'grid'
})
});
if (!hasPosts) {
return createElement("div", blockProps, inspectorControls, createElement(Placeholder, {
icon: pin,
label: __('Latest Posts')
}, !Array.isArray(latestPosts) ? createElement(Spinner, null) : __('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: __('List view'),
onClick: () => setAttributes({
postLayout: 'list'
}),
isActive: postLayout === 'list'
}, {
icon: grid,
title: __('Grid view'),
onClick: () => setAttributes({
postLayout: 'grid'
}),
isActive: postLayout === 'grid'
}];
const dateFormat = getSettings().formats.date;
return createElement("div", null, inspectorControls, createElement(BlockControls, null, createElement(ToolbarGroup, {
controls: layoutControls
})), createElement("ul", blockProps, displayPosts.map((post, i) => {
const titleTrimmed = post.title.rendered.trim();
let excerpt = post.excerpt.rendered;
const currentAuthor = authorList === null || authorList === void 0 ? void 0 : authorList.find(author => author.id === post.author);
const excerptElement = document.createElement('div');
excerptElement.innerHTML = excerpt;
excerpt = excerptElement.textContent || excerptElement.innerText || '';
const {
url: imageSourceUrl,
alt: featuredImageAlt
} = getFeaturedImageDetails(post, featuredImageSizeSlug);
const imageClasses = classnames({
'wp-block-latest-posts__featured-image': true,
[`align${featuredImageAlign}`]: !!featuredImageAlign
});
const renderFeaturedImage = displayFeaturedImage && imageSourceUrl;
const featuredImage = renderFeaturedImage && createElement("img", {
src: imageSourceUrl,
alt: featuredImageAlt,
style: {
maxWidth: featuredImageSizeWidth,
maxHeight: featuredImageSizeHeight
}
});
const needsReadMore = excerptLength < excerpt.trim().split(' ').length && post.excerpt.raw === '';
const postExcerpt = needsReadMore ? createElement(Fragment, null, excerpt.trim().split(' ', excerptLength).join(' '), __(' … '), createElement("a", {
href: post.link,
rel: "noopener noreferrer",
onClick: showRedirectionPreventedNotice
}, __('Read more'))) : excerpt;
return createElement("li", {
key: i
}, renderFeaturedImage && createElement("div", {
className: imageClasses
}, addLinkToFeaturedImage ? createElement("a", {
className: "wp-block-latest-posts__post-title",
href: post.link,
rel: "noreferrer noopener",
onClick: showRedirectionPreventedNotice
}, featuredImage) : featuredImage), createElement("a", {
href: post.link,
rel: "noreferrer noopener",
dangerouslySetInnerHTML: !!titleTrimmed ? {
__html: titleTrimmed
} : undefined,
onClick: showRedirectionPreventedNotice
}, !titleTrimmed ? __('(no title)') : null), displayAuthor && currentAuthor && createElement("div", {
className: "wp-block-latest-posts__post-author"
}, sprintf(
/* translators: byline. %s: current author. */
__('by %s'), currentAuthor.name)), displayPostDate && post.date_gmt && createElement("time", {
dateTime: format('c', post.date_gmt),
className: "wp-block-latest-posts__post-date"
}, dateI18n(dateFormat, post.date_gmt)), displayPostContent && displayPostContentRadio === 'excerpt' && createElement("div", {
className: "wp-block-latest-posts__post-excerpt"
}, postExcerpt), displayPostContent && displayPostContentRadio === 'full_post' && createElement("div", {
className: "wp-block-latest-posts__post-full-content",
dangerouslySetInnerHTML: {
__html: post.content.raw.trim()
}
}));
})));
}
//# sourceMappingURL=edit.js.map