@wordpress/edit-site 6.3.0 → 6.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (221) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/build/components/add-new-pattern/index.js +8 -2
  3. package/build/components/add-new-pattern/index.js.map +1 -1
  4. package/build/components/add-new-template/index.js +3 -1
  5. package/build/components/add-new-template/index.js.map +1 -1
  6. package/build/components/block-editor/use-site-editor-settings.js +1 -82
  7. package/build/components/block-editor/use-site-editor-settings.js.map +1 -1
  8. package/build/components/global-styles/block-preview-panel.js +14 -5
  9. package/build/components/global-styles/block-preview-panel.js.map +1 -1
  10. package/build/components/global-styles/font-families.js +42 -23
  11. package/build/components/global-styles/font-families.js.map +1 -1
  12. package/build/components/global-styles/font-library-modal/index.js +4 -4
  13. package/build/components/global-styles/font-library-modal/index.js.map +1 -1
  14. package/build/components/global-styles/font-library-modal/installed-fonts.js +58 -10
  15. package/build/components/global-styles/font-library-modal/installed-fonts.js.map +1 -1
  16. package/build/components/global-styles/font-sizes/confirm-delete-font-size-dialog.js +39 -0
  17. package/build/components/global-styles/font-sizes/confirm-delete-font-size-dialog.js.map +1 -0
  18. package/build/components/global-styles/font-sizes/confirm-reset-font-sizes-dialog.js +39 -0
  19. package/build/components/global-styles/font-sizes/confirm-reset-font-sizes-dialog.js.map +1 -0
  20. package/build/components/global-styles/font-sizes/font-size-preview.js +44 -0
  21. package/build/components/global-styles/font-sizes/font-size-preview.js.map +1 -0
  22. package/build/components/global-styles/font-sizes/font-size.js +213 -0
  23. package/build/components/global-styles/font-sizes/font-size.js.map +1 -0
  24. package/build/components/global-styles/font-sizes/font-sizes-count.js +50 -0
  25. package/build/components/global-styles/font-sizes/font-sizes-count.js.map +1 -0
  26. package/build/components/global-styles/font-sizes/font-sizes.js +163 -0
  27. package/build/components/global-styles/font-sizes/font-sizes.js.map +1 -0
  28. package/build/components/global-styles/font-sizes/rename-font-size-dialog.js +67 -0
  29. package/build/components/global-styles/font-sizes/rename-font-size-dialog.js.map +1 -0
  30. package/build/components/global-styles/screen-block.js +10 -8
  31. package/build/components/global-styles/screen-block.js.map +1 -1
  32. package/build/components/global-styles/screen-style-variations.js +2 -2
  33. package/build/components/global-styles/screen-style-variations.js.map +1 -1
  34. package/build/components/global-styles/screen-typography.js +3 -2
  35. package/build/components/global-styles/screen-typography.js.map +1 -1
  36. package/build/components/global-styles/size-control/index.js +85 -0
  37. package/build/components/global-styles/size-control/index.js.map +1 -0
  38. package/build/components/global-styles/style-variations-container.js +3 -0
  39. package/build/components/global-styles/style-variations-container.js.map +1 -1
  40. package/build/components/global-styles/ui.js +8 -0
  41. package/build/components/global-styles/ui.js.map +1 -1
  42. package/build/components/global-styles/variations/variations-color.js.map +1 -1
  43. package/build/components/global-styles/variations/variations-typography.js +1 -1
  44. package/build/components/global-styles/variations/variations-typography.js.map +1 -1
  45. package/build/components/layout/index.js +6 -0
  46. package/build/components/layout/index.js.map +1 -1
  47. package/build/components/layout/router.js +14 -6
  48. package/build/components/layout/router.js.map +1 -1
  49. package/build/components/page-patterns/header.js +1 -1
  50. package/build/components/page-patterns/header.js.map +1 -1
  51. package/build/components/page-patterns/index.js +23 -11
  52. package/build/components/page-patterns/index.js.map +1 -1
  53. package/build/components/page-templates/index.js +41 -34
  54. package/build/components/page-templates/index.js.map +1 -1
  55. package/build/components/post-edit/index.js +105 -0
  56. package/build/components/post-edit/index.js.map +1 -0
  57. package/build/components/post-fields/index.js +314 -0
  58. package/build/components/post-fields/index.js.map +1 -0
  59. package/build/components/post-list/index.js +281 -0
  60. package/build/components/post-list/index.js.map +1 -0
  61. package/build/components/posts-app/router.js +3 -3
  62. package/build/components/posts-app/router.js.map +1 -1
  63. package/build/components/sidebar-dataviews/default-views.js +22 -10
  64. package/build/components/sidebar-dataviews/default-views.js.map +1 -1
  65. package/build/components/sidebar-dataviews/index.js +40 -1
  66. package/build/components/sidebar-dataviews/index.js.map +1 -1
  67. package/build/components/sidebar-navigation-screen-global-styles/content.js +64 -0
  68. package/build/components/sidebar-navigation-screen-global-styles/content.js.map +1 -0
  69. package/build/components/sidebar-navigation-screen-global-styles/index.js +2 -47
  70. package/build/components/sidebar-navigation-screen-global-styles/index.js.map +1 -1
  71. package/build/hooks/push-changes-to-global-styles/index.js +1 -1
  72. package/build/hooks/push-changes-to-global-styles/index.js.map +1 -1
  73. package/build/index.js +5 -1
  74. package/build/index.js.map +1 -1
  75. package/build/store/selectors.js +34 -6
  76. package/build/store/selectors.js.map +1 -1
  77. package/build/utils/get-filtered-template-parts.js +64 -0
  78. package/build/utils/get-filtered-template-parts.js.map +1 -0
  79. package/build-module/components/add-new-pattern/index.js +8 -2
  80. package/build-module/components/add-new-pattern/index.js.map +1 -1
  81. package/build-module/components/add-new-template/index.js +3 -1
  82. package/build-module/components/add-new-template/index.js.map +1 -1
  83. package/build-module/components/block-editor/use-site-editor-settings.js +1 -82
  84. package/build-module/components/block-editor/use-site-editor-settings.js.map +1 -1
  85. package/build-module/components/global-styles/block-preview-panel.js +14 -5
  86. package/build-module/components/global-styles/block-preview-panel.js.map +1 -1
  87. package/build-module/components/global-styles/font-families.js +44 -25
  88. package/build-module/components/global-styles/font-families.js.map +1 -1
  89. package/build-module/components/global-styles/font-library-modal/index.js +4 -4
  90. package/build-module/components/global-styles/font-library-modal/index.js.map +1 -1
  91. package/build-module/components/global-styles/font-library-modal/installed-fonts.js +61 -13
  92. package/build-module/components/global-styles/font-library-modal/installed-fonts.js.map +1 -1
  93. package/build-module/components/global-styles/font-sizes/confirm-delete-font-size-dialog.js +32 -0
  94. package/build-module/components/global-styles/font-sizes/confirm-delete-font-size-dialog.js.map +1 -0
  95. package/build-module/components/global-styles/font-sizes/confirm-reset-font-sizes-dialog.js +32 -0
  96. package/build-module/components/global-styles/font-sizes/confirm-reset-font-sizes-dialog.js.map +1 -0
  97. package/build-module/components/global-styles/font-sizes/font-size-preview.js +37 -0
  98. package/build-module/components/global-styles/font-sizes/font-size-preview.js.map +1 -0
  99. package/build-module/components/global-styles/font-sizes/font-size.js +207 -0
  100. package/build-module/components/global-styles/font-sizes/font-size.js.map +1 -0
  101. package/build-module/components/global-styles/font-sizes/font-sizes-count.js +43 -0
  102. package/build-module/components/global-styles/font-sizes/font-sizes-count.js.map +1 -0
  103. package/build-module/components/global-styles/font-sizes/font-sizes.js +157 -0
  104. package/build-module/components/global-styles/font-sizes/font-sizes.js.map +1 -0
  105. package/build-module/components/global-styles/font-sizes/rename-font-size-dialog.js +61 -0
  106. package/build-module/components/global-styles/font-sizes/rename-font-size-dialog.js.map +1 -0
  107. package/build-module/components/global-styles/screen-block.js +10 -8
  108. package/build-module/components/global-styles/screen-block.js.map +1 -1
  109. package/build-module/components/global-styles/screen-style-variations.js +2 -2
  110. package/build-module/components/global-styles/screen-style-variations.js.map +1 -1
  111. package/build-module/components/global-styles/screen-typography.js +3 -2
  112. package/build-module/components/global-styles/screen-typography.js.map +1 -1
  113. package/build-module/components/global-styles/size-control/index.js +79 -0
  114. package/build-module/components/global-styles/size-control/index.js.map +1 -0
  115. package/build-module/components/global-styles/style-variations-container.js +3 -0
  116. package/build-module/components/global-styles/style-variations-container.js.map +1 -1
  117. package/build-module/components/global-styles/ui.js +8 -0
  118. package/build-module/components/global-styles/ui.js.map +1 -1
  119. package/build-module/components/global-styles/variations/variations-color.js +1 -1
  120. package/build-module/components/global-styles/variations/variations-color.js.map +1 -1
  121. package/build-module/components/global-styles/variations/variations-typography.js +1 -2
  122. package/build-module/components/global-styles/variations/variations-typography.js.map +1 -1
  123. package/build-module/components/layout/index.js +6 -0
  124. package/build-module/components/layout/index.js.map +1 -1
  125. package/build-module/components/layout/router.js +14 -6
  126. package/build-module/components/layout/router.js.map +1 -1
  127. package/build-module/components/page-patterns/header.js +1 -1
  128. package/build-module/components/page-patterns/header.js.map +1 -1
  129. package/build-module/components/page-patterns/index.js +23 -11
  130. package/build-module/components/page-patterns/index.js.map +1 -1
  131. package/build-module/components/page-templates/index.js +43 -37
  132. package/build-module/components/page-templates/index.js.map +1 -1
  133. package/build-module/components/post-edit/index.js +98 -0
  134. package/build-module/components/post-edit/index.js.map +1 -0
  135. package/build-module/components/post-fields/index.js +306 -0
  136. package/build-module/components/post-fields/index.js.map +1 -0
  137. package/build-module/components/post-list/index.js +275 -0
  138. package/build-module/components/post-list/index.js.map +1 -0
  139. package/build-module/components/posts-app/router.js +3 -3
  140. package/build-module/components/posts-app/router.js.map +1 -1
  141. package/build-module/components/sidebar-dataviews/default-views.js +21 -9
  142. package/build-module/components/sidebar-dataviews/default-views.js.map +1 -1
  143. package/build-module/components/sidebar-dataviews/index.js +42 -3
  144. package/build-module/components/sidebar-dataviews/index.js.map +1 -1
  145. package/build-module/components/sidebar-navigation-screen-global-styles/content.js +57 -0
  146. package/build-module/components/sidebar-navigation-screen-global-styles/content.js.map +1 -0
  147. package/build-module/components/sidebar-navigation-screen-global-styles/index.js +2 -47
  148. package/build-module/components/sidebar-navigation-screen-global-styles/index.js.map +1 -1
  149. package/build-module/hooks/push-changes-to-global-styles/index.js +1 -1
  150. package/build-module/hooks/push-changes-to-global-styles/index.js.map +1 -1
  151. package/build-module/index.js +5 -1
  152. package/build-module/index.js.map +1 -1
  153. package/build-module/store/selectors.js +35 -7
  154. package/build-module/store/selectors.js.map +1 -1
  155. package/build-module/utils/get-filtered-template-parts.js +57 -0
  156. package/build-module/utils/get-filtered-template-parts.js.map +1 -0
  157. package/build-style/posts-rtl.css +581 -503
  158. package/build-style/posts.css +581 -503
  159. package/build-style/style-rtl.css +621 -519
  160. package/build-style/style.css +621 -519
  161. package/package.json +41 -41
  162. package/src/components/add-new-pattern/index.js +8 -2
  163. package/src/components/add-new-template/index.js +4 -1
  164. package/src/components/add-new-template/style.scss +4 -6
  165. package/src/components/block-editor/use-site-editor-settings.js +15 -111
  166. package/src/components/global-styles/block-preview-panel.js +22 -9
  167. package/src/components/global-styles/font-families.js +66 -31
  168. package/src/components/global-styles/font-library-modal/index.js +4 -2
  169. package/src/components/global-styles/font-library-modal/installed-fonts.js +92 -11
  170. package/src/components/global-styles/font-library-modal/style.scss +9 -0
  171. package/src/components/global-styles/font-sizes/confirm-delete-font-size-dialog.js +43 -0
  172. package/src/components/global-styles/font-sizes/confirm-reset-font-sizes-dialog.js +37 -0
  173. package/src/components/global-styles/font-sizes/font-size-preview.js +43 -0
  174. package/src/components/global-styles/font-sizes/font-size.js +250 -0
  175. package/src/components/global-styles/font-sizes/font-sizes-count.js +40 -0
  176. package/src/components/global-styles/font-sizes/font-sizes.js +263 -0
  177. package/src/components/global-styles/font-sizes/rename-font-size-dialog.js +70 -0
  178. package/src/components/global-styles/screen-block.js +12 -14
  179. package/src/components/global-styles/screen-style-variations.js +2 -2
  180. package/src/components/global-styles/screen-typography.js +3 -2
  181. package/src/components/global-styles/size-control/index.js +86 -0
  182. package/src/components/global-styles/style-variations-container.js +4 -0
  183. package/src/components/global-styles/style.scss +13 -3
  184. package/src/components/global-styles/ui.js +10 -0
  185. package/src/components/global-styles/variations/variations-color.js +1 -1
  186. package/src/components/global-styles/variations/variations-typography.js +1 -2
  187. package/src/components/layout/index.js +11 -0
  188. package/src/components/layout/router.js +13 -5
  189. package/src/components/layout/style.scss +26 -8
  190. package/src/components/page-patterns/header.js +1 -1
  191. package/src/components/page-patterns/index.js +15 -8
  192. package/src/components/page-templates/index.js +51 -46
  193. package/src/components/page-templates/style.scss +5 -3
  194. package/src/components/post-edit/index.js +96 -0
  195. package/src/components/post-edit/style.scss +9 -0
  196. package/src/components/post-fields/index.js +345 -0
  197. package/src/components/post-list/index.js +326 -0
  198. package/src/components/{posts-app → post-list}/style.scss +12 -9
  199. package/src/components/posts-app/router.js +3 -3
  200. package/src/components/sidebar-dataviews/default-views.js +21 -9
  201. package/src/components/sidebar-dataviews/index.js +36 -1
  202. package/src/components/sidebar-navigation-screen-global-styles/content.js +55 -0
  203. package/src/components/sidebar-navigation-screen-global-styles/index.js +1 -55
  204. package/src/hooks/push-changes-to-global-styles/index.js +1 -1
  205. package/src/index.js +7 -1
  206. package/src/posts.scss +1 -1
  207. package/src/store/selectors.js +53 -14
  208. package/src/store/test/selectors.js +1 -26
  209. package/src/style.scss +2 -1
  210. package/src/utils/get-filtered-template-parts.js +61 -0
  211. package/src/utils/test/get-filtered-template-parts.js +127 -0
  212. package/build/components/global-styles/screen-background.js +0 -36
  213. package/build/components/global-styles/screen-background.js.map +0 -1
  214. package/build/components/posts-app/posts-list.js +0 -568
  215. package/build/components/posts-app/posts-list.js.map +0 -1
  216. package/build-module/components/global-styles/screen-background.js +0 -30
  217. package/build-module/components/global-styles/screen-background.js.map +0 -1
  218. package/build-module/components/posts-app/posts-list.js +0 -560
  219. package/build-module/components/posts-app/posts-list.js.map +0 -1
  220. package/src/components/global-styles/screen-background.js +0 -29
  221. package/src/components/posts-app/posts-list.js +0 -651
@@ -0,0 +1,345 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import clsx from 'clsx';
5
+
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import { __, sprintf } from '@wordpress/i18n';
10
+ import { decodeEntities } from '@wordpress/html-entities';
11
+ import {
12
+ createInterpolateElement,
13
+ useMemo,
14
+ useState,
15
+ } from '@wordpress/element';
16
+ import { dateI18n, getDate, getSettings } from '@wordpress/date';
17
+ import {
18
+ trash,
19
+ drafts,
20
+ published,
21
+ scheduled,
22
+ pending,
23
+ notAllowed,
24
+ commentAuthorAvatar as authorIcon,
25
+ } from '@wordpress/icons';
26
+ import { __experimentalHStack as HStack, Icon } from '@wordpress/components';
27
+ import { useSelect } from '@wordpress/data';
28
+ import { useEntityRecords, store as coreStore } from '@wordpress/core-data';
29
+
30
+ /**
31
+ * Internal dependencies
32
+ */
33
+ import {
34
+ LAYOUT_GRID,
35
+ LAYOUT_TABLE,
36
+ LAYOUT_LIST,
37
+ OPERATOR_IS_ANY,
38
+ } from '../../utils/constants';
39
+ import { default as Link, useLink } from '../routes/link';
40
+ import Media from '../media';
41
+
42
+ // See https://github.com/WordPress/gutenberg/issues/55886
43
+ // We do not support custom statutes at the moment.
44
+ const STATUSES = [
45
+ { value: 'draft', label: __( 'Draft' ), icon: drafts },
46
+ { value: 'future', label: __( 'Scheduled' ), icon: scheduled },
47
+ { value: 'pending', label: __( 'Pending Review' ), icon: pending },
48
+ { value: 'private', label: __( 'Private' ), icon: notAllowed },
49
+ { value: 'publish', label: __( 'Published' ), icon: published },
50
+ { value: 'trash', label: __( 'Trash' ), icon: trash },
51
+ ];
52
+
53
+ const getFormattedDate = ( dateToDisplay ) =>
54
+ dateI18n(
55
+ getSettings().formats.datetimeAbbreviated,
56
+ getDate( dateToDisplay )
57
+ );
58
+
59
+ function FeaturedImage( { item, viewType } ) {
60
+ const isDisabled = item.status === 'trash';
61
+ const { onClick } = useLink( {
62
+ postId: item.id,
63
+ postType: item.type,
64
+ canvas: 'edit',
65
+ } );
66
+ const hasMedia = !! item.featured_media;
67
+ const size =
68
+ viewType === LAYOUT_GRID
69
+ ? [ 'large', 'full', 'medium', 'thumbnail' ]
70
+ : [ 'thumbnail', 'medium', 'large', 'full' ];
71
+ const media = hasMedia ? (
72
+ <Media
73
+ className="edit-site-post-list__featured-image"
74
+ id={ item.featured_media }
75
+ size={ size }
76
+ />
77
+ ) : null;
78
+ const renderButton = viewType !== LAYOUT_LIST && ! isDisabled;
79
+ return (
80
+ <div
81
+ className={ `edit-site-post-list__featured-image-wrapper is-layout-${ viewType }` }
82
+ >
83
+ { renderButton ? (
84
+ <button
85
+ className="edit-site-post-list__featured-image-button"
86
+ type="button"
87
+ onClick={ onClick }
88
+ aria-label={ item.title?.rendered || __( '(no title)' ) }
89
+ >
90
+ { media }
91
+ </button>
92
+ ) : (
93
+ media
94
+ ) }
95
+ </div>
96
+ );
97
+ }
98
+
99
+ function PostStatusField( { item } ) {
100
+ const status = STATUSES.find( ( { value } ) => value === item.status );
101
+ const label = status?.label || item.status;
102
+ const icon = status?.icon;
103
+ return (
104
+ <HStack alignment="left" spacing={ 0 }>
105
+ { icon && (
106
+ <div className="edit-site-post-list__status-icon">
107
+ <Icon icon={ icon } />
108
+ </div>
109
+ ) }
110
+ <span>{ label }</span>
111
+ </HStack>
112
+ );
113
+ }
114
+
115
+ function PostAuthorField( { item } ) {
116
+ const { text, imageUrl } = useSelect(
117
+ ( select ) => {
118
+ const { getUser } = select( coreStore );
119
+ const user = getUser( item.author );
120
+ return {
121
+ imageUrl: user?.avatar_urls?.[ 48 ],
122
+ text: user?.name,
123
+ };
124
+ },
125
+ [ item ]
126
+ );
127
+ const [ isImageLoaded, setIsImageLoaded ] = useState( false );
128
+ return (
129
+ <HStack alignment="left" spacing={ 0 }>
130
+ { !! imageUrl && (
131
+ <div
132
+ className={ clsx( 'page-templates-author-field__avatar', {
133
+ 'is-loaded': isImageLoaded,
134
+ } ) }
135
+ >
136
+ <img
137
+ onLoad={ () => setIsImageLoaded( true ) }
138
+ alt={ __( 'Author avatar' ) }
139
+ src={ imageUrl }
140
+ />
141
+ </div>
142
+ ) }
143
+ { ! imageUrl && (
144
+ <div className="page-templates-author-field__icon">
145
+ <Icon icon={ authorIcon } />
146
+ </div>
147
+ ) }
148
+ <span className="page-templates-author-field__name">{ text }</span>
149
+ </HStack>
150
+ );
151
+ }
152
+
153
+ function usePostFields( viewType ) {
154
+ const { records: authors, isResolving: isLoadingAuthors } =
155
+ useEntityRecords( 'root', 'user', { per_page: -1 } );
156
+
157
+ const { frontPageId, postsPageId } = useSelect( ( select ) => {
158
+ const { getEntityRecord } = select( coreStore );
159
+ const siteSettings = getEntityRecord( 'root', 'site' );
160
+ return {
161
+ frontPageId: siteSettings?.page_on_front,
162
+ postsPageId: siteSettings?.page_for_posts,
163
+ };
164
+ }, [] );
165
+
166
+ const fields = useMemo(
167
+ () => [
168
+ {
169
+ id: 'featured-image',
170
+ label: __( 'Featured Image' ),
171
+ getValue: ( { item } ) => item.featured_media,
172
+ render: ( { item } ) => (
173
+ <FeaturedImage item={ item } viewType={ viewType } />
174
+ ),
175
+ enableSorting: false,
176
+ },
177
+ {
178
+ label: __( 'Title' ),
179
+ id: 'title',
180
+ type: 'text',
181
+ getValue: ( { item } ) =>
182
+ typeof item.title === 'string'
183
+ ? item.title
184
+ : item.title?.raw,
185
+ render: ( { item } ) => {
186
+ const addLink =
187
+ [ LAYOUT_TABLE, LAYOUT_GRID ].includes( viewType ) &&
188
+ item.status !== 'trash';
189
+ const title = addLink ? (
190
+ <Link
191
+ params={ {
192
+ postId: item.id,
193
+ postType: item.type,
194
+ canvas: 'edit',
195
+ } }
196
+ >
197
+ { decodeEntities( item.title?.rendered ) ||
198
+ __( '(no title)' ) }
199
+ </Link>
200
+ ) : (
201
+ <span>
202
+ { decodeEntities( item.title?.rendered ) ||
203
+ __( '(no title)' ) }
204
+ </span>
205
+ );
206
+
207
+ let suffix = '';
208
+ if ( item.id === frontPageId ) {
209
+ suffix = (
210
+ <span className="edit-site-post-list__title-badge">
211
+ { __( 'Homepage' ) }
212
+ </span>
213
+ );
214
+ } else if ( item.id === postsPageId ) {
215
+ suffix = (
216
+ <span className="edit-site-post-list__title-badge">
217
+ { __( 'Posts Page' ) }
218
+ </span>
219
+ );
220
+ }
221
+
222
+ return (
223
+ <HStack
224
+ className="edit-site-post-list__title"
225
+ alignment="center"
226
+ justify="flex-start"
227
+ >
228
+ { title }
229
+ { suffix }
230
+ </HStack>
231
+ );
232
+ },
233
+ enableHiding: false,
234
+ },
235
+ {
236
+ label: __( 'Author' ),
237
+ id: 'author',
238
+ getValue: ( { item } ) => item._embedded?.author[ 0 ]?.name,
239
+ elements:
240
+ authors?.map( ( { id, name } ) => ( {
241
+ value: id,
242
+ label: name,
243
+ } ) ) || [],
244
+ render: PostAuthorField,
245
+ },
246
+ {
247
+ label: __( 'Status' ),
248
+ id: 'status',
249
+ getValue: ( { item } ) =>
250
+ STATUSES.find( ( { value } ) => value === item.status )
251
+ ?.label ?? item.status,
252
+ elements: STATUSES,
253
+ render: PostStatusField,
254
+ enableSorting: false,
255
+ filterBy: {
256
+ operators: [ OPERATOR_IS_ANY ],
257
+ },
258
+ },
259
+ {
260
+ label: __( 'Date' ),
261
+ id: 'date',
262
+ render: ( { item } ) => {
263
+ const isDraftOrPrivate = [ 'draft', 'private' ].includes(
264
+ item.status
265
+ );
266
+ if ( isDraftOrPrivate ) {
267
+ return createInterpolateElement(
268
+ sprintf(
269
+ /* translators: %s: page creation date */
270
+ __( '<span>Modified: <time>%s</time></span>' ),
271
+ getFormattedDate( item.date )
272
+ ),
273
+ {
274
+ span: <span />,
275
+ time: <time />,
276
+ }
277
+ );
278
+ }
279
+
280
+ const isScheduled = item.status === 'future';
281
+ if ( isScheduled ) {
282
+ return createInterpolateElement(
283
+ sprintf(
284
+ /* translators: %s: page creation date */
285
+ __( '<span>Scheduled: <time>%s</time></span>' ),
286
+ getFormattedDate( item.date )
287
+ ),
288
+ {
289
+ span: <span />,
290
+ time: <time />,
291
+ }
292
+ );
293
+ }
294
+
295
+ // Pending & Published posts show the modified date if it's newer.
296
+ const dateToDisplay =
297
+ getDate( item.modified ) > getDate( item.date )
298
+ ? item.modified
299
+ : item.date;
300
+
301
+ const isPending = item.status === 'pending';
302
+ if ( isPending ) {
303
+ return createInterpolateElement(
304
+ sprintf(
305
+ /* translators: %s: the newest of created or modified date for the page */
306
+ __( '<span>Modified: <time>%s</time></span>' ),
307
+ getFormattedDate( dateToDisplay )
308
+ ),
309
+ {
310
+ span: <span />,
311
+ time: <time />,
312
+ }
313
+ );
314
+ }
315
+
316
+ const isPublished = item.status === 'publish';
317
+ if ( isPublished ) {
318
+ return createInterpolateElement(
319
+ sprintf(
320
+ /* translators: %s: the newest of created or modified date for the page */
321
+ __( '<span>Published: <time>%s</time></span>' ),
322
+ getFormattedDate( dateToDisplay )
323
+ ),
324
+ {
325
+ span: <span />,
326
+ time: <time />,
327
+ }
328
+ );
329
+ }
330
+
331
+ // Unknow status.
332
+ return <time>{ getFormattedDate( item.date ) }</time>;
333
+ },
334
+ },
335
+ ],
336
+ [ authors, viewType, frontPageId, postsPageId ]
337
+ );
338
+
339
+ return {
340
+ isLoading: isLoadingAuthors,
341
+ fields,
342
+ };
343
+ }
344
+
345
+ export default usePostFields;
@@ -0,0 +1,326 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { Button } from '@wordpress/components';
5
+ import { useEntityRecords, store as coreStore } from '@wordpress/core-data';
6
+ import { useState, useMemo, useCallback, useEffect } from '@wordpress/element';
7
+ import { privateApis as routerPrivateApis } from '@wordpress/router';
8
+ import { useSelect, useDispatch } from '@wordpress/data';
9
+ import { DataViews } from '@wordpress/dataviews';
10
+ import { privateApis as editorPrivateApis } from '@wordpress/editor';
11
+ import { __ } from '@wordpress/i18n';
12
+ import { drawerRight } from '@wordpress/icons';
13
+
14
+ /**
15
+ * Internal dependencies
16
+ */
17
+ import Page from '../page';
18
+ import {
19
+ useDefaultViews,
20
+ defaultLayouts,
21
+ } from '../sidebar-dataviews/default-views';
22
+ import {
23
+ OPERATOR_IS_ANY,
24
+ OPERATOR_IS_NONE,
25
+ LAYOUT_LIST,
26
+ } from '../../utils/constants';
27
+
28
+ import AddNewPostModal from '../add-new-post';
29
+ import { unlock } from '../../lock-unlock';
30
+ import { useEditPostAction } from '../dataviews-actions';
31
+ import { usePrevious } from '@wordpress/compose';
32
+ import usePostFields from '../post-fields';
33
+
34
+ const { usePostActions } = unlock( editorPrivateApis );
35
+ const { useLocation, useHistory } = unlock( routerPrivateApis );
36
+ const EMPTY_ARRAY = [];
37
+
38
+ function useView( postType ) {
39
+ const {
40
+ params: { activeView = 'all', isCustom = 'false', layout },
41
+ } = useLocation();
42
+ const history = useHistory();
43
+ const DEFAULT_VIEWS = useDefaultViews( { postType } );
44
+ const selectedDefaultView = useMemo( () => {
45
+ const defaultView =
46
+ isCustom === 'false' &&
47
+ DEFAULT_VIEWS[ postType ].find(
48
+ ( { slug } ) => slug === activeView
49
+ )?.view;
50
+ if ( isCustom === 'false' && layout ) {
51
+ return {
52
+ ...defaultView,
53
+ type: layout,
54
+ layout: defaultLayouts[ layout ]?.layout,
55
+ };
56
+ }
57
+ return defaultView;
58
+ }, [ isCustom, activeView, layout, postType, DEFAULT_VIEWS ] );
59
+ const [ view, setView ] = useState( selectedDefaultView );
60
+
61
+ useEffect( () => {
62
+ if ( selectedDefaultView ) {
63
+ setView( selectedDefaultView );
64
+ }
65
+ }, [ selectedDefaultView ] );
66
+ const editedViewRecord = useSelect(
67
+ ( select ) => {
68
+ if ( isCustom !== 'true' ) {
69
+ return;
70
+ }
71
+ const { getEditedEntityRecord } = select( coreStore );
72
+ const dataviewRecord = getEditedEntityRecord(
73
+ 'postType',
74
+ 'wp_dataviews',
75
+ Number( activeView )
76
+ );
77
+ return dataviewRecord;
78
+ },
79
+ [ activeView, isCustom ]
80
+ );
81
+ const { editEntityRecord } = useDispatch( coreStore );
82
+
83
+ const customView = useMemo( () => {
84
+ const storedView =
85
+ editedViewRecord?.content &&
86
+ JSON.parse( editedViewRecord?.content );
87
+ if ( ! storedView ) {
88
+ return storedView;
89
+ }
90
+
91
+ return {
92
+ ...storedView,
93
+ layout: defaultLayouts[ storedView?.type ]?.layout,
94
+ };
95
+ }, [ editedViewRecord?.content ] );
96
+
97
+ const setCustomView = useCallback(
98
+ ( viewToSet ) => {
99
+ editEntityRecord(
100
+ 'postType',
101
+ 'wp_dataviews',
102
+ editedViewRecord?.id,
103
+ {
104
+ content: JSON.stringify( viewToSet ),
105
+ }
106
+ );
107
+ },
108
+ [ editEntityRecord, editedViewRecord?.id ]
109
+ );
110
+
111
+ const setDefaultViewAndUpdateUrl = useCallback(
112
+ ( viewToSet ) => {
113
+ if ( viewToSet.type !== view?.type ) {
114
+ const { params } = history.getLocationWithParams();
115
+ history.push( {
116
+ ...params,
117
+ layout: viewToSet.type,
118
+ } );
119
+ }
120
+ setView( viewToSet );
121
+ },
122
+ [ history, view?.type ]
123
+ );
124
+
125
+ if ( isCustom === 'false' ) {
126
+ return [ view, setDefaultViewAndUpdateUrl ];
127
+ } else if ( isCustom === 'true' && customView ) {
128
+ return [ customView, setCustomView ];
129
+ }
130
+ // Loading state where no the view was not found on custom views or default views.
131
+ return [ DEFAULT_VIEWS[ postType ][ 0 ].view, setDefaultViewAndUpdateUrl ];
132
+ }
133
+
134
+ const DEFAULT_STATUSES = 'draft,future,pending,private,publish'; // All but 'trash'.
135
+
136
+ function getItemId( item ) {
137
+ return item.id.toString();
138
+ }
139
+
140
+ export default function PostList( { postType } ) {
141
+ const [ view, setView ] = useView( postType );
142
+ const history = useHistory();
143
+ const location = useLocation();
144
+ const { postId, quickEdit = false } = location.params;
145
+ const [ selection, setSelection ] = useState( postId?.split( ',' ) ?? [] );
146
+ const onChangeSelection = useCallback(
147
+ ( items ) => {
148
+ setSelection( items );
149
+ const { params } = history.getLocationWithParams();
150
+ if ( ( params.isCustom ?? 'false' ) === 'false' ) {
151
+ history.push( {
152
+ ...params,
153
+ postId: items.join( ',' ),
154
+ } );
155
+ }
156
+ },
157
+ [ history ]
158
+ );
159
+
160
+ const queryArgs = useMemo( () => {
161
+ const filters = {};
162
+ view.filters.forEach( ( filter ) => {
163
+ if (
164
+ filter.field === 'status' &&
165
+ filter.operator === OPERATOR_IS_ANY
166
+ ) {
167
+ filters.status = filter.value;
168
+ }
169
+ if (
170
+ filter.field === 'author' &&
171
+ filter.operator === OPERATOR_IS_ANY
172
+ ) {
173
+ filters.author = filter.value;
174
+ } else if (
175
+ filter.field === 'author' &&
176
+ filter.operator === OPERATOR_IS_NONE
177
+ ) {
178
+ filters.author_exclude = filter.value;
179
+ }
180
+ } );
181
+ // We want to provide a different default item for the status filter
182
+ // than the REST API provides.
183
+ if ( ! filters.status || filters.status === '' ) {
184
+ filters.status = DEFAULT_STATUSES;
185
+ }
186
+
187
+ return {
188
+ per_page: view.perPage,
189
+ page: view.page,
190
+ _embed: 'author',
191
+ order: view.sort?.direction,
192
+ orderby: view.sort?.field,
193
+ search: view.search,
194
+ ...filters,
195
+ };
196
+ }, [ view ] );
197
+ const {
198
+ records,
199
+ isResolving: isLoadingMainEntities,
200
+ totalItems,
201
+ totalPages,
202
+ } = useEntityRecords( 'postType', postType, queryArgs );
203
+
204
+ const ids = records?.map( ( record ) => getItemId( record ) ) ?? [];
205
+ const prevIds = usePrevious( ids ) ?? [];
206
+ const deletedIds = prevIds.filter( ( id ) => ! ids.includes( id ) );
207
+ const postIdWasDeleted = deletedIds.includes( postId );
208
+
209
+ useEffect( () => {
210
+ if ( postIdWasDeleted ) {
211
+ history.push( {
212
+ ...history.getLocationWithParams().params,
213
+ postId: undefined,
214
+ } );
215
+ }
216
+ }, [ postIdWasDeleted, history ] );
217
+
218
+ const paginationInfo = useMemo(
219
+ () => ( {
220
+ totalItems,
221
+ totalPages,
222
+ } ),
223
+ [ totalItems, totalPages ]
224
+ );
225
+
226
+ const { labels, canCreateRecord } = useSelect(
227
+ ( select ) => {
228
+ const { getPostType, canUser } = select( coreStore );
229
+ return {
230
+ labels: getPostType( postType )?.labels,
231
+ canCreateRecord: canUser( 'create', {
232
+ kind: 'postType',
233
+ name: postType,
234
+ } ),
235
+ };
236
+ },
237
+ [ postType ]
238
+ );
239
+
240
+ const postTypeActions = usePostActions( {
241
+ postType,
242
+ context: 'list',
243
+ } );
244
+ const editAction = useEditPostAction();
245
+ const actions = useMemo(
246
+ () => [ editAction, ...postTypeActions ],
247
+ [ postTypeActions, editAction ]
248
+ );
249
+
250
+ const [ showAddPostModal, setShowAddPostModal ] = useState( false );
251
+
252
+ const openModal = () => setShowAddPostModal( true );
253
+ const closeModal = () => setShowAddPostModal( false );
254
+ const handleNewPage = ( { type, id } ) => {
255
+ history.push( {
256
+ postId: id,
257
+ postType: type,
258
+ canvas: 'edit',
259
+ } );
260
+ closeModal();
261
+ };
262
+ const { isLoading: isLoadingFields, fields } = usePostFields( view.type );
263
+
264
+ return (
265
+ <Page
266
+ title={ labels?.name }
267
+ actions={
268
+ labels?.add_new_item &&
269
+ canCreateRecord && (
270
+ <>
271
+ <Button
272
+ variant="primary"
273
+ onClick={ openModal }
274
+ __next40pxDefaultSize
275
+ >
276
+ { labels.add_new_item }
277
+ </Button>
278
+ { showAddPostModal && (
279
+ <AddNewPostModal
280
+ postType={ postType }
281
+ onSave={ handleNewPage }
282
+ onClose={ closeModal }
283
+ />
284
+ ) }
285
+ </>
286
+ )
287
+ }
288
+ >
289
+ <DataViews
290
+ paginationInfo={ paginationInfo }
291
+ fields={ fields }
292
+ actions={ actions }
293
+ data={ records || EMPTY_ARRAY }
294
+ isLoading={ isLoadingMainEntities || isLoadingFields }
295
+ view={ view }
296
+ onChangeView={ setView }
297
+ selection={ selection }
298
+ onChangeSelection={ onChangeSelection }
299
+ getItemId={ getItemId }
300
+ defaultLayouts={ defaultLayouts }
301
+ header={
302
+ window.__experimentalQuickEditDataViews &&
303
+ view.type !== LAYOUT_LIST &&
304
+ postType === 'page' && (
305
+ <Button
306
+ size="compact"
307
+ isPressed={ quickEdit }
308
+ icon={ drawerRight }
309
+ label={
310
+ ! quickEdit
311
+ ? __( 'Show quick edit sidebar' )
312
+ : __( 'Close quick edit sidebar' )
313
+ }
314
+ onClick={ () => {
315
+ history.push( {
316
+ ...location.params,
317
+ quickEdit: quickEdit ? undefined : true,
318
+ } );
319
+ } }
320
+ />
321
+ )
322
+ }
323
+ />
324
+ </Page>
325
+ );
326
+ }