UNPKG

5.61 kBJavaScriptView Raw
1/**
2 * External dependencies
3 */
4import { isUndefined, pickBy } from 'lodash';
5import classnames from 'classnames';
6
7/**
8 * WordPress dependencies
9 */
10import {
11 Component,
12 Fragment,
13 RawHTML,
14} from '@wordpress/element';
15import {
16 PanelBody,
17 Placeholder,
18 QueryControls,
19 RangeControl,
20 Spinner,
21 ToggleControl,
22 Toolbar,
23} from '@wordpress/components';
24import apiFetch from '@wordpress/api-fetch';
25import { addQueryArgs } from '@wordpress/url';
26import { __ } from '@wordpress/i18n';
27import { dateI18n, format, __experimentalGetSettings } from '@wordpress/date';
28import {
29 InspectorControls,
30 BlockAlignmentToolbar,
31 BlockControls,
32} from '@wordpress/block-editor';
33import { withSelect } from '@wordpress/data';
34
35/**
36 * Module Constants
37 */
38const CATEGORIES_LIST_QUERY = {
39 per_page: -1,
40};
41const MAX_POSTS_COLUMNS = 6;
42
43class LatestPostsEdit extends Component {
44 constructor() {
45 super( ...arguments );
46 this.state = {
47 categoriesList: [],
48 };
49 this.toggleDisplayPostDate = this.toggleDisplayPostDate.bind( this );
50 }
51
52 componentWillMount() {
53 this.isStillMounted = true;
54 this.fetchRequest = apiFetch( {
55 path: addQueryArgs( `/wp/v2/categories`, CATEGORIES_LIST_QUERY ),
56 } ).then(
57 ( categoriesList ) => {
58 if ( this.isStillMounted ) {
59 this.setState( { categoriesList } );
60 }
61 }
62 ).catch(
63 () => {
64 if ( this.isStillMounted ) {
65 this.setState( { categoriesList: [] } );
66 }
67 }
68 );
69 }
70
71 componentWillUnmount() {
72 this.isStillMounted = false;
73 }
74
75 toggleDisplayPostDate() {
76 const { displayPostDate } = this.props.attributes;
77 const { setAttributes } = this.props;
78
79 setAttributes( { displayPostDate: ! displayPostDate } );
80 }
81
82 render() {
83 const { attributes, setAttributes, latestPosts } = this.props;
84 const { categoriesList } = this.state;
85 const { displayPostDate, align, postLayout, columns, order, orderBy, categories, postsToShow } = attributes;
86
87 const inspectorControls = (
88 <InspectorControls>
89 <PanelBody title={ __( 'Latest Posts Settings' ) }>
90 <QueryControls
91 { ...{ order, orderBy } }
92 numberOfItems={ postsToShow }
93 categoriesList={ categoriesList }
94 selectedCategoryId={ categories }
95 onOrderChange={ ( value ) => setAttributes( { order: value } ) }
96 onOrderByChange={ ( value ) => setAttributes( { orderBy: value } ) }
97 onCategoryChange={ ( value ) => setAttributes( { categories: '' !== value ? value : undefined } ) }
98 onNumberOfItemsChange={ ( value ) => setAttributes( { postsToShow: value } ) }
99 />
100 <ToggleControl
101 label={ __( 'Display post date' ) }
102 checked={ displayPostDate }
103 onChange={ this.toggleDisplayPostDate }
104 />
105 { postLayout === 'grid' &&
106 <RangeControl
107 label={ __( 'Columns' ) }
108 value={ columns }
109 onChange={ ( value ) => setAttributes( { columns: value } ) }
110 min={ 2 }
111 max={ ! hasPosts ? MAX_POSTS_COLUMNS : Math.min( MAX_POSTS_COLUMNS, latestPosts.length ) }
112 required
113 />
114 }
115 </PanelBody>
116 </InspectorControls>
117 );
118
119 const hasPosts = Array.isArray( latestPosts ) && latestPosts.length;
120 if ( ! hasPosts ) {
121 return (
122 <Fragment>
123 { inspectorControls }
124 <Placeholder
125 icon="admin-post"
126 label={ __( 'Latest Posts' ) }
127 >
128 { ! Array.isArray( latestPosts ) ?
129 <Spinner /> :
130 __( 'No posts found.' )
131 }
132 </Placeholder>
133 </Fragment>
134 );
135 }
136
137 // Removing posts from display should be instant.
138 const displayPosts = latestPosts.length > postsToShow ?
139 latestPosts.slice( 0, postsToShow ) :
140 latestPosts;
141
142 const layoutControls = [
143 {
144 icon: 'list-view',
145 title: __( 'List View' ),
146 onClick: () => setAttributes( { postLayout: 'list' } ),
147 isActive: postLayout === 'list',
148 },
149 {
150 icon: 'grid-view',
151 title: __( 'Grid View' ),
152 onClick: () => setAttributes( { postLayout: 'grid' } ),
153 isActive: postLayout === 'grid',
154 },
155 ];
156
157 const dateFormat = __experimentalGetSettings().formats.date;
158
159 return (
160 <Fragment>
161 { inspectorControls }
162 <BlockControls>
163 <BlockAlignmentToolbar
164 value={ align }
165 onChange={ ( nextAlign ) => {
166 setAttributes( { align: nextAlign } );
167 } }
168 />
169 <Toolbar controls={ layoutControls } />
170 </BlockControls>
171 <ul
172 className={ classnames( this.props.className, {
173 'is-grid': postLayout === 'grid',
174 'has-dates': displayPostDate,
175 [ `columns-${ columns }` ]: postLayout === 'grid',
176 } ) }
177 >
178 { displayPosts.map( ( post, i ) => {
179 const titleTrimmed = post.title.rendered.trim();
180 return (
181 <li key={ i }>
182 <a href={ post.link } target="_blank" rel="noreferrer noopener">
183 { titleTrimmed ? (
184 <RawHTML>
185 { titleTrimmed }
186 </RawHTML>
187 ) :
188 __( '(Untitled)' )
189 }
190 </a>
191 { displayPostDate && post.date_gmt &&
192 <time dateTime={ format( 'c', post.date_gmt ) } className="wp-block-latest-posts__post-date">
193 { dateI18n( dateFormat, post.date_gmt ) }
194 </time>
195 }
196 </li>
197 );
198 } ) }
199 </ul>
200 </Fragment>
201 );
202 }
203}
204
205export default withSelect( ( select, props ) => {
206 const { postsToShow, order, orderBy, categories } = props.attributes;
207 const { getEntityRecords } = select( 'core' );
208 const latestPostsQuery = pickBy( {
209 categories,
210 order,
211 orderby: orderBy,
212 per_page: postsToShow,
213 }, ( value ) => ! isUndefined( value ) );
214 return {
215 latestPosts: getEntityRecords( 'postType', 'post', latestPostsQuery ),
216 };
217} )( LatestPostsEdit );