diff --git a/package-lock.json b/package-lock.json index 27f01fd681824f..a4c740c9c1c993 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21155,6 +21155,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", "engines": { "node": ">=6" } @@ -54701,6 +54702,7 @@ "@wordpress/warning": "*", "change-case": "4.1.2", "client-zip": "^2.4.5", + "clsx": "2.1.1", "remove-accents": "^0.5.0" }, "engines": { diff --git a/packages/edit-site/src/components/post-fields/index.js b/packages/edit-site/src/components/post-fields/index.js index e151c5a048c011..6ba9453709f0d3 100644 --- a/packages/edit-site/src/components/post-fields/index.js +++ b/packages/edit-site/src/components/post-fields/index.js @@ -1,12 +1,6 @@ -/** - * External dependencies - */ -import clsx from 'clsx'; - /** * WordPress dependencies */ -import { __ } from '@wordpress/i18n'; import { featuredImageField, slugField, @@ -16,50 +10,10 @@ import { commentStatusField, titleField, dateField, + authorField, } from '@wordpress/fields'; -import { useMemo, useState } from '@wordpress/element'; -import { commentAuthorAvatar as authorIcon } from '@wordpress/icons'; -import { __experimentalHStack as HStack, Icon } from '@wordpress/components'; -import { useSelect } from '@wordpress/data'; -import { useEntityRecords, store as coreStore } from '@wordpress/core-data'; - -function PostAuthorField( { item } ) { - const { text, imageUrl } = useSelect( - ( select ) => { - const { getUser } = select( coreStore ); - const user = getUser( item.author ); - return { - imageUrl: user?.avatar_urls?.[ 48 ], - text: user?.name, - }; - }, - [ item ] - ); - const [ isImageLoaded, setIsImageLoaded ] = useState( false ); - return ( - - { !! imageUrl && ( - - setIsImageLoaded( true ) } - alt={ __( 'Author avatar' ) } - src={ imageUrl } - /> - - ) } - { ! imageUrl && ( - - - - ) } - { text } - - ); -} +import { useMemo } from '@wordpress/element'; +import { useEntityRecords } from '@wordpress/core-data'; function usePostFields() { const { records: authors, isResolving: isLoadingAuthors } = @@ -70,23 +24,12 @@ function usePostFields() { featuredImageField, titleField, { - label: __( 'Author' ), - id: 'author', - type: 'integer', + ...authorField, elements: authors?.map( ( { id, name } ) => ( { value: id, label: name, } ) ) || [], - render: PostAuthorField, - sort: ( a, b, direction ) => { - const nameA = a._embedded?.author?.[ 0 ]?.name || ''; - const nameB = b._embedded?.author?.[ 0 ]?.name || ''; - - return direction === 'asc' - ? nameA.localeCompare( nameB ) - : nameB.localeCompare( nameA ); - }, }, statusField, dateField, diff --git a/packages/fields/README.md b/packages/fields/README.md index bfcc63be4da472..e6cf6d3007ed97 100644 --- a/packages/fields/README.md +++ b/packages/fields/README.md @@ -14,6 +14,10 @@ npm install @wordpress/fields --save +### authorField + +Author field for BasePost. + ### commentStatusField Comment status field for BasePost. diff --git a/packages/fields/package.json b/packages/fields/package.json index f32c779c2e989e..cee1060223767a 100644 --- a/packages/fields/package.json +++ b/packages/fields/package.json @@ -57,6 +57,7 @@ "@wordpress/warning": "*", "change-case": "4.1.2", "client-zip": "^2.4.5", + "clsx": "2.1.1", "remove-accents": "^0.5.0" }, "peerDependencies": { diff --git a/packages/fields/src/fields/author/author-view.tsx b/packages/fields/src/fields/author/author-view.tsx new file mode 100644 index 00000000000000..8232350af5193e --- /dev/null +++ b/packages/fields/src/fields/author/author-view.tsx @@ -0,0 +1,63 @@ +/** + * External dependencies + */ +import clsx from 'clsx'; + +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { useState } from '@wordpress/element'; +import { commentAuthorAvatar as authorIcon } from '@wordpress/icons'; +import { __experimentalHStack as HStack, Icon } from '@wordpress/components'; +import { useSelect } from '@wordpress/data'; +import { store as coreStore } from '@wordpress/core-data'; +import type { User } from '@wordpress/core-data'; + +/** + * Internal dependencies + */ +import type { BasePostWithEmbeddedAuthor } from '../../types'; + +function AuthorView( { item }: { item: BasePostWithEmbeddedAuthor } ) { + const { text, imageUrl } = useSelect( + ( select ) => { + const { getEntityRecord } = select( coreStore ); + let user: User | undefined; + if ( !! item.author ) { + user = getEntityRecord( 'root', 'user', item.author ); + } + return { + imageUrl: user?.avatar_urls?.[ 48 ], + text: user?.name, + }; + }, + [ item ] + ); + const [ isImageLoaded, setIsImageLoaded ] = useState( false ); + return ( + + { !! imageUrl && ( + + setIsImageLoaded( true ) } + alt={ __( 'Author avatar' ) } + src={ imageUrl } + /> + + ) } + { ! imageUrl && ( + + + + ) } + { text } + + ); +} + +export default AuthorView; diff --git a/packages/fields/src/fields/author/index.tsx b/packages/fields/src/fields/author/index.tsx new file mode 100644 index 00000000000000..22f9409e051556 --- /dev/null +++ b/packages/fields/src/fields/author/index.tsx @@ -0,0 +1,32 @@ +/** + * WordPress dependencies + */ +import type { Field } from '@wordpress/dataviews'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import type { BasePostWithEmbeddedAuthor } from '../../types'; +import AuthorView from './author-view'; + +const authorField: Field< BasePostWithEmbeddedAuthor > = { + label: __( 'Author' ), + id: 'author', + type: 'integer', + elements: [], + render: AuthorView, + sort: ( a, b, direction ) => { + const nameA = a._embedded?.author?.[ 0 ]?.name || ''; + const nameB = b._embedded?.author?.[ 0 ]?.name || ''; + + return direction === 'asc' + ? nameA.localeCompare( nameB ) + : nameB.localeCompare( nameA ); + }, +}; + +/** + * Author field for BasePost. + */ +export default authorField; diff --git a/packages/fields/src/fields/index.ts b/packages/fields/src/fields/index.ts index 36e064006232bb..5ea4235af1d964 100644 --- a/packages/fields/src/fields/index.ts +++ b/packages/fields/src/fields/index.ts @@ -7,3 +7,4 @@ export { default as passwordField } from './password'; export { default as statusField } from './status'; export { default as commentStatusField } from './comment-status'; export { default as dateField } from './date'; +export { default as authorField } from './author'; diff --git a/packages/fields/src/types.ts b/packages/fields/src/types.ts index 71831b6aab180c..e457ec699554cd 100644 --- a/packages/fields/src/types.ts +++ b/packages/fields/src/types.ts @@ -23,6 +23,15 @@ interface Links { [ key: string ]: { href: string }[] | undefined; } +interface Author { + name: string; + avatar_urls: Record< string, string >; +} + +interface EmbeddedAuthor { + author: Author[]; +} + export interface BasePost extends CommonPost { comment_status?: 'open' | 'closed'; excerpt?: string | { raw: string; rendered: string }; @@ -39,6 +48,11 @@ export interface BasePost extends CommonPost { permalink_template?: string; date?: string; modified?: string; + author?: number; +} + +export interface BasePostWithEmbeddedAuthor extends BasePost { + _embedded: EmbeddedAuthor; } export interface Template extends CommonPost {