UNPKG

@wordpress/block-library

Version:
710 lines (709 loc) 24.1 kB
// packages/block-library/src/latest-posts/edit.js import clsx from "clsx"; 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"; import { MIN_EXCERPT_LENGTH, MAX_EXCERPT_LENGTH, MAX_POSTS_COLUMNS, DEFAULT_EXCERPT_LENGTH } from "./constants"; import { useToolsPanelDropdownMenuProps } from "../utils/hooks"; import { Fragment, jsx, jsxs } from "react/jsx-runtime"; var CATEGORIES_LIST_QUERY = { per_page: -1, _fields: "id,name", context: "view" }; var USERS_LIST_QUERY = { per_page: -1, has_published_posts: ["post"], context: "view" }; var 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) { const image = post._embedded?.["wp:featuredmedia"]?.["0"]; return { url: image?.media_details?.sizes?.[size]?.source_url ?? image?.source_url, alt: image?.alt_text }; } function getCurrentAuthor(post) { return post._embedded?.author?.[0]; } function Controls({ attributes, setAttributes, postCount }) { 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) => { const { getEntityRecords, getUsers } = select(coreStore); const settings = select(blockEditorStore).getSettings(); return { defaultImageWidth: settings.imageDimensions?.[featuredImageSizeSlug]?.width ?? 0, defaultImageHeight: settings.imageDimensions?.[featuredImageSizeSlug]?.height ?? 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?.reduce( (accumulator, category) => ({ ...accumulator, [category.name]: category }), {} ) ?? {}; const selectCategories = (tokens) => { const hasNoSuggestion = tokens.some( (token) => typeof token === "string" && !categorySuggestions[token] ); if (hasNoSuggestion) { return; } const allCategories = tokens.map((token) => { return typeof token === "string" ? categorySuggestions[token] : token; }); 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, 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, 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: void 0, featuredImageSizeSlug: "thumbnail", featuredImageSizeWidth: null, featuredImageSizeHeight: null, addLinkToFeaturedImage: false }), 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, imageSizeHelp: __( "Select the size of the source image." ), onChangeImage: (value) => setAttributes({ featuredImageSizeSlug: value, featuredImageSizeWidth: void 0, featuredImageSizeHeight: void 0 }) } ) } ), /* @__PURE__ */ jsx( ToolsPanelItem, { hasValue: () => !!featuredImageAlign, label: __("Image alignment"), onDeselect: () => setAttributes({ featuredImageAlign: void 0 }), 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 : void 0 }), children: imageAlignmentOptions.map( ({ value, icon, label }) => { return /* @__PURE__ */ jsx( ToggleGroupControlOptionIcon, { value, icon, 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: void 0, selectedAuthor: void 0, columns: 3 }), 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: void 0, selectedAuthor: void 0 }), 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, onCategoryChange: selectCategories, selectedCategories: categories, onAuthorChange: (value) => setAttributes({ selectedAuthor: "" !== value ? Number(value) : void 0 }), authorList: 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 } ) } ) ] } ) ] }); } function LatestPostsEdit({ attributes, setAttributes }) { 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] ); 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, setAttributes, postCount: 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.") }) ] }); } 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 */ __( "\u2026 <a>Read more<span>: %1$s</span></a>" ), titleTrimmed || __("(no title)") ), { a: ( // eslint-disable-next-line jsx-a11y/anchor-has-content /* @__PURE__ */ 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, onClick: showRedirectionPreventedNotice, children: featuredImage } ) : featuredImage }), /* @__PURE__ */ jsx( "a", { className: "wp-block-latest-posts__post-title", href: post.link, dangerouslySetInnerHTML: !!titleTrimmed ? { __html: titleTrimmed } : void 0, 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); }) }) ] }); } export { LatestPostsEdit as default }; //# sourceMappingURL=edit.js.map