@wordpress/edit-site 6.35.1-next.16d95556a.0 → 6.36.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 (65) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/build/components/add-new-template-legacy/add-custom-generic-template-modal-content.js +109 -0
  3. package/build/components/add-new-template-legacy/add-custom-generic-template-modal-content.js.map +7 -0
  4. package/build/components/add-new-template-legacy/add-custom-template-modal-content.js +328 -0
  5. package/build/components/add-new-template-legacy/add-custom-template-modal-content.js.map +7 -0
  6. package/build/components/add-new-template-legacy/index.js +389 -0
  7. package/build/components/add-new-template-legacy/index.js.map +7 -0
  8. package/build/components/add-new-template-legacy/utils.js +620 -0
  9. package/build/components/add-new-template-legacy/utils.js.map +7 -0
  10. package/build/components/page-templates/fields.js +3 -1
  11. package/build/components/page-templates/fields.js.map +2 -2
  12. package/build/components/page-templates/index-legacy.js +185 -0
  13. package/build/components/page-templates/index-legacy.js.map +7 -0
  14. package/build/components/post-list/index.js +10 -0
  15. package/build/components/post-list/index.js.map +2 -2
  16. package/build/components/sidebar-navigation-screen-templates-browse/content-legacy.js +101 -0
  17. package/build/components/sidebar-navigation-screen-templates-browse/content-legacy.js.map +7 -0
  18. package/build/components/sidebar-navigation-screen-templates-browse/index.js +2 -1
  19. package/build/components/sidebar-navigation-screen-templates-browse/index.js.map +3 -3
  20. package/build/components/site-editor-routes/templates.js +10 -2
  21. package/build/components/site-editor-routes/templates.js.map +3 -3
  22. package/build/components/site-hub/index.js +1 -1
  23. package/build/components/site-hub/index.js.map +2 -2
  24. package/build/utils/constants.js +6 -0
  25. package/build/utils/constants.js.map +2 -2
  26. package/build-module/components/add-new-template-legacy/add-custom-generic-template-modal-content.js +93 -0
  27. package/build-module/components/add-new-template-legacy/add-custom-generic-template-modal-content.js.map +7 -0
  28. package/build-module/components/add-new-template-legacy/add-custom-template-modal-content.js +316 -0
  29. package/build-module/components/add-new-template-legacy/add-custom-template-modal-content.js.map +7 -0
  30. package/build-module/components/add-new-template-legacy/index.js +390 -0
  31. package/build-module/components/add-new-template-legacy/index.js.map +7 -0
  32. package/build-module/components/add-new-template-legacy/utils.js +589 -0
  33. package/build-module/components/add-new-template-legacy/utils.js.map +7 -0
  34. package/build-module/components/page-templates/fields.js +3 -1
  35. package/build-module/components/page-templates/fields.js.map +2 -2
  36. package/build-module/components/page-templates/index-legacy.js +154 -0
  37. package/build-module/components/page-templates/index-legacy.js.map +7 -0
  38. package/build-module/components/post-list/index.js +12 -0
  39. package/build-module/components/post-list/index.js.map +2 -2
  40. package/build-module/components/sidebar-navigation-screen-templates-browse/content-legacy.js +70 -0
  41. package/build-module/components/sidebar-navigation-screen-templates-browse/content-legacy.js.map +7 -0
  42. package/build-module/components/sidebar-navigation-screen-templates-browse/index.js +2 -1
  43. package/build-module/components/sidebar-navigation-screen-templates-browse/index.js.map +2 -2
  44. package/build-module/components/site-editor-routes/templates.js +10 -2
  45. package/build-module/components/site-editor-routes/templates.js.map +2 -2
  46. package/build-module/components/site-hub/index.js +1 -1
  47. package/build-module/components/site-hub/index.js.map +2 -2
  48. package/build-module/utils/constants.js +4 -0
  49. package/build-module/utils/constants.js.map +2 -2
  50. package/build-style/style-rtl.css +309 -40
  51. package/build-style/style.css +309 -40
  52. package/package.json +46 -46
  53. package/src/components/add-new-template-legacy/add-custom-generic-template-modal-content.js +94 -0
  54. package/src/components/add-new-template-legacy/add-custom-template-modal-content.js +314 -0
  55. package/src/components/add-new-template-legacy/index.js +454 -0
  56. package/src/components/add-new-template-legacy/utils.js +760 -0
  57. package/src/components/page-templates/fields.js +13 -9
  58. package/src/components/page-templates/index-legacy.js +166 -0
  59. package/src/components/post-list/index.js +13 -0
  60. package/src/components/sidebar-navigation-screen-templates-browse/content-legacy.js +79 -0
  61. package/src/components/sidebar-navigation-screen-templates-browse/index.js +8 -1
  62. package/src/components/site-editor-routes/templates.js +22 -3
  63. package/src/components/site-hub/index.js +1 -1
  64. package/src/components/welcome-guide/style.scss +8 -0
  65. package/src/utils/constants.js +2 -0
@@ -0,0 +1,454 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import clsx from 'clsx';
5
+
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import {
10
+ Button,
11
+ Modal,
12
+ __experimentalGrid as Grid,
13
+ __experimentalText as Text,
14
+ __experimentalVStack as VStack,
15
+ Flex,
16
+ Icon,
17
+ } from '@wordpress/components';
18
+ import { decodeEntities } from '@wordpress/html-entities';
19
+ import { useState, memo, useRef, useEffect } from '@wordpress/element';
20
+ import { useSelect, useDispatch } from '@wordpress/data';
21
+ import { store as coreStore } from '@wordpress/core-data';
22
+ import { useViewportMatch } from '@wordpress/compose';
23
+ import {
24
+ archive,
25
+ blockMeta,
26
+ calendar,
27
+ category,
28
+ commentAuthorAvatar,
29
+ pencil,
30
+ home,
31
+ layout,
32
+ list,
33
+ media,
34
+ notFound,
35
+ page,
36
+ pin,
37
+ verse,
38
+ search,
39
+ tag,
40
+ } from '@wordpress/icons';
41
+ import { __, sprintf } from '@wordpress/i18n';
42
+ import { store as noticesStore } from '@wordpress/notices';
43
+ import { privateApis as routerPrivateApis } from '@wordpress/router';
44
+ import { focus } from '@wordpress/dom';
45
+
46
+ /**
47
+ * Internal dependencies
48
+ */
49
+ import { TEMPLATE_POST_TYPE } from '../../utils/constants';
50
+
51
+ /**
52
+ * Internal dependencies
53
+ */
54
+ import AddCustomTemplateModalContent from './add-custom-template-modal-content';
55
+ import {
56
+ useExistingTemplates,
57
+ useDefaultTemplateTypes,
58
+ useTaxonomiesMenuItems,
59
+ usePostTypeMenuItems,
60
+ useAuthorMenuItem,
61
+ usePostTypeArchiveMenuItems,
62
+ } from './utils';
63
+ import AddCustomGenericTemplateModalContent from './add-custom-generic-template-modal-content';
64
+ import { unlock } from '../../lock-unlock';
65
+
66
+ const { useHistory } = unlock( routerPrivateApis );
67
+
68
+ const DEFAULT_TEMPLATE_SLUGS = [
69
+ 'front-page',
70
+ 'home',
71
+ 'single',
72
+ 'page',
73
+ 'index',
74
+ 'archive',
75
+ 'author',
76
+ 'category',
77
+ 'date',
78
+ 'tag',
79
+ 'search',
80
+ '404',
81
+ ];
82
+
83
+ const TEMPLATE_ICONS = {
84
+ 'front-page': home,
85
+ home: verse,
86
+ single: pin,
87
+ page,
88
+ archive,
89
+ search,
90
+ 404: notFound,
91
+ index: list,
92
+ category,
93
+ author: commentAuthorAvatar,
94
+ taxonomy: blockMeta,
95
+ date: calendar,
96
+ tag,
97
+ attachment: media,
98
+ };
99
+
100
+ function TemplateListItem( {
101
+ title,
102
+ direction,
103
+ className,
104
+ description,
105
+ icon,
106
+ onClick,
107
+ children,
108
+ } ) {
109
+ return (
110
+ <Button
111
+ __next40pxDefaultSize
112
+ className={ className }
113
+ onClick={ onClick }
114
+ label={ description }
115
+ showTooltip={ !! description }
116
+ >
117
+ <Flex
118
+ as="span"
119
+ spacing={ 2 }
120
+ align="center"
121
+ justify="center"
122
+ style={ { width: '100%' } }
123
+ direction={ direction }
124
+ >
125
+ <div className="edit-site-add-new-template__template-icon">
126
+ <Icon icon={ icon } />
127
+ </div>
128
+ <VStack
129
+ className="edit-site-add-new-template__template-name"
130
+ alignment="center"
131
+ spacing={ 0 }
132
+ >
133
+ <Text
134
+ align="center"
135
+ weight={ 500 }
136
+ lineHeight={ 1.53846153846 } // 20px
137
+ >
138
+ { title }
139
+ </Text>
140
+ { children }
141
+ </VStack>
142
+ </Flex>
143
+ </Button>
144
+ );
145
+ }
146
+
147
+ const modalContentMap = {
148
+ templatesList: 1,
149
+ customTemplate: 2,
150
+ customGenericTemplate: 3,
151
+ };
152
+
153
+ function NewTemplateModal( { onClose } ) {
154
+ const [ modalContent, setModalContent ] = useState(
155
+ modalContentMap.templatesList
156
+ );
157
+ const [ entityForSuggestions, setEntityForSuggestions ] = useState( {} );
158
+ const [ isSubmitting, setIsSubmitting ] = useState( false );
159
+ const missingTemplates = useMissingTemplates( setEntityForSuggestions, () =>
160
+ setModalContent( modalContentMap.customTemplate )
161
+ );
162
+ const history = useHistory();
163
+ const { saveEntityRecord } = useDispatch( coreStore );
164
+ const { createErrorNotice, createSuccessNotice } =
165
+ useDispatch( noticesStore );
166
+ const containerRef = useRef( null );
167
+ const isMobile = useViewportMatch( 'medium', '<' );
168
+
169
+ const homeUrl = useSelect( ( select ) => {
170
+ // Site index.
171
+ return select( coreStore ).getEntityRecord( 'root', '__unstableBase' )
172
+ ?.home;
173
+ }, [] );
174
+
175
+ const TEMPLATE_SHORT_DESCRIPTIONS = {
176
+ 'front-page': homeUrl,
177
+ date: sprintf(
178
+ // translators: %s: The homepage url.
179
+ __( 'E.g. %s' ),
180
+ homeUrl + '/' + new Date().getFullYear()
181
+ ),
182
+ };
183
+
184
+ useEffect( () => {
185
+ // Focus the first focusable element when component mounts or UI changes
186
+ // We don't want to focus on the other modals because they have their own focus management.
187
+ if (
188
+ containerRef.current &&
189
+ modalContent === modalContentMap.templatesList
190
+ ) {
191
+ const [ firstFocusable ] = focus.focusable.find(
192
+ containerRef.current
193
+ );
194
+ firstFocusable?.focus();
195
+ }
196
+ }, [ modalContent ] );
197
+
198
+ async function createTemplate( template, isWPSuggestion = true ) {
199
+ if ( isSubmitting ) {
200
+ return;
201
+ }
202
+ setIsSubmitting( true );
203
+ try {
204
+ const { title, description, slug } = template;
205
+ const newTemplate = await saveEntityRecord(
206
+ 'postType',
207
+ TEMPLATE_POST_TYPE,
208
+ {
209
+ description,
210
+ // Slugs need to be strings, so this is for template `404`
211
+ slug: slug.toString(),
212
+ status: 'publish',
213
+ title,
214
+ // This adds a post meta field in template that is part of `is_custom` value calculation.
215
+ is_wp_suggestion: isWPSuggestion,
216
+ },
217
+ { throwOnError: true }
218
+ );
219
+
220
+ // Navigate to the created template editor.
221
+ history.navigate(
222
+ `/${ TEMPLATE_POST_TYPE }/${ newTemplate.id }?canvas=edit`
223
+ );
224
+
225
+ createSuccessNotice(
226
+ sprintf(
227
+ // translators: %s: Title of the created post or template, e.g: "Hello world".
228
+ __( '"%s" successfully created.' ),
229
+ decodeEntities( newTemplate.title?.rendered || title ) ||
230
+ __( '(no title)' )
231
+ ),
232
+ {
233
+ type: 'snackbar',
234
+ }
235
+ );
236
+ } catch ( error ) {
237
+ const errorMessage =
238
+ error.message && error.code !== 'unknown_error'
239
+ ? error.message
240
+ : __( 'An error occurred while creating the template.' );
241
+
242
+ createErrorNotice( errorMessage, {
243
+ type: 'snackbar',
244
+ } );
245
+ } finally {
246
+ setIsSubmitting( false );
247
+ }
248
+ }
249
+ const onModalClose = () => {
250
+ onClose();
251
+ setModalContent( modalContentMap.templatesList );
252
+ };
253
+
254
+ let modalTitle = __( 'Add template' );
255
+ if ( modalContent === modalContentMap.customTemplate ) {
256
+ modalTitle = sprintf(
257
+ // translators: %s: Name of the post type e.g: "Post".
258
+ __( 'Add template: %s' ),
259
+ entityForSuggestions.labels.singular_name
260
+ );
261
+ } else if ( modalContent === modalContentMap.customGenericTemplate ) {
262
+ modalTitle = __( 'Create custom template' );
263
+ }
264
+
265
+ return (
266
+ <Modal
267
+ title={ modalTitle }
268
+ className={ clsx( 'edit-site-add-new-template__modal', {
269
+ 'edit-site-add-new-template__modal_template_list':
270
+ modalContent === modalContentMap.templatesList,
271
+ 'edit-site-custom-template-modal':
272
+ modalContent === modalContentMap.customTemplate,
273
+ } ) }
274
+ onRequestClose={ onModalClose }
275
+ overlayClassName={
276
+ modalContent === modalContentMap.customGenericTemplate
277
+ ? 'edit-site-custom-generic-template__modal'
278
+ : undefined
279
+ }
280
+ ref={ containerRef }
281
+ >
282
+ { modalContent === modalContentMap.templatesList && (
283
+ <Grid
284
+ columns={ isMobile ? 2 : 3 }
285
+ gap={ 4 }
286
+ align="flex-start"
287
+ justify="center"
288
+ className="edit-site-add-new-template__template-list__contents"
289
+ >
290
+ <Flex className="edit-site-add-new-template__template-list__prompt">
291
+ { __(
292
+ 'Select what the new template should apply to:'
293
+ ) }
294
+ </Flex>
295
+ { missingTemplates.map( ( template ) => {
296
+ const { title, slug, onClick } = template;
297
+ return (
298
+ <TemplateListItem
299
+ key={ slug }
300
+ title={ title }
301
+ direction="column"
302
+ className="edit-site-add-new-template__template-button"
303
+ description={
304
+ TEMPLATE_SHORT_DESCRIPTIONS[ slug ]
305
+ }
306
+ icon={ TEMPLATE_ICONS[ slug ] || layout }
307
+ onClick={ () =>
308
+ onClick
309
+ ? onClick( template )
310
+ : createTemplate( template )
311
+ }
312
+ />
313
+ );
314
+ } ) }
315
+ <TemplateListItem
316
+ title={ __( 'Custom template' ) }
317
+ direction="row"
318
+ className="edit-site-add-new-template__custom-template-button"
319
+ icon={ pencil }
320
+ onClick={ () =>
321
+ setModalContent(
322
+ modalContentMap.customGenericTemplate
323
+ )
324
+ }
325
+ >
326
+ <Text
327
+ lineHeight={ 1.53846153846 } // 20px
328
+ >
329
+ { __(
330
+ 'A custom template can be manually applied to any post or page.'
331
+ ) }
332
+ </Text>
333
+ </TemplateListItem>
334
+ </Grid>
335
+ ) }
336
+ { modalContent === modalContentMap.customTemplate && (
337
+ <AddCustomTemplateModalContent
338
+ onSelect={ createTemplate }
339
+ entityForSuggestions={ entityForSuggestions }
340
+ onBack={ () =>
341
+ setModalContent( modalContentMap.templatesList )
342
+ }
343
+ containerRef={ containerRef }
344
+ />
345
+ ) }
346
+ { modalContent === modalContentMap.customGenericTemplate && (
347
+ <AddCustomGenericTemplateModalContent
348
+ createTemplate={ createTemplate }
349
+ onBack={ () =>
350
+ setModalContent( modalContentMap.templatesList )
351
+ }
352
+ />
353
+ ) }
354
+ </Modal>
355
+ );
356
+ }
357
+
358
+ function NewTemplate() {
359
+ const [ showModal, setShowModal ] = useState( false );
360
+
361
+ const { postType } = useSelect( ( select ) => {
362
+ const { getPostType } = select( coreStore );
363
+
364
+ return {
365
+ postType: getPostType( TEMPLATE_POST_TYPE ),
366
+ };
367
+ }, [] );
368
+
369
+ if ( ! postType ) {
370
+ return null;
371
+ }
372
+
373
+ return (
374
+ <>
375
+ <Button
376
+ variant="primary"
377
+ onClick={ () => setShowModal( true ) }
378
+ label={ postType.labels.add_new_item }
379
+ __next40pxDefaultSize
380
+ >
381
+ { postType.labels.add_new_item }
382
+ </Button>
383
+ { showModal && (
384
+ <NewTemplateModal onClose={ () => setShowModal( false ) } />
385
+ ) }
386
+ </>
387
+ );
388
+ }
389
+
390
+ function useMissingTemplates( setEntityForSuggestions, onClick ) {
391
+ const existingTemplates = useExistingTemplates();
392
+ const defaultTemplateTypes = useDefaultTemplateTypes();
393
+ const existingTemplateSlugs = ( existingTemplates || [] ).map(
394
+ ( { slug } ) => slug
395
+ );
396
+ const missingDefaultTemplates = ( defaultTemplateTypes || [] ).filter(
397
+ ( template ) =>
398
+ DEFAULT_TEMPLATE_SLUGS.includes( template.slug ) &&
399
+ ! existingTemplateSlugs.includes( template.slug )
400
+ );
401
+ const onClickMenuItem = ( _entityForSuggestions ) => {
402
+ onClick?.();
403
+ setEntityForSuggestions( _entityForSuggestions );
404
+ };
405
+ // We need to replace existing default template types with
406
+ // the create specific template functionality. The original
407
+ // info (title, description, etc.) is preserved in the
408
+ // used hooks.
409
+ const enhancedMissingDefaultTemplateTypes = [ ...missingDefaultTemplates ];
410
+ const { defaultTaxonomiesMenuItems, taxonomiesMenuItems } =
411
+ useTaxonomiesMenuItems( onClickMenuItem );
412
+ const { defaultPostTypesMenuItems, postTypesMenuItems } =
413
+ usePostTypeMenuItems( onClickMenuItem );
414
+
415
+ const authorMenuItem = useAuthorMenuItem( onClickMenuItem );
416
+ [
417
+ ...defaultTaxonomiesMenuItems,
418
+ ...defaultPostTypesMenuItems,
419
+ authorMenuItem,
420
+ ].forEach( ( menuItem ) => {
421
+ if ( ! menuItem ) {
422
+ return;
423
+ }
424
+ const matchIndex = enhancedMissingDefaultTemplateTypes.findIndex(
425
+ ( template ) => template.slug === menuItem.slug
426
+ );
427
+ // Some default template types might have been filtered above from
428
+ // `missingDefaultTemplates` because they only check for the general
429
+ // template. So here we either replace or append the item, augmented
430
+ // with the check if it has available specific item to create a
431
+ // template for.
432
+ if ( matchIndex > -1 ) {
433
+ enhancedMissingDefaultTemplateTypes[ matchIndex ] = menuItem;
434
+ } else {
435
+ enhancedMissingDefaultTemplateTypes.push( menuItem );
436
+ }
437
+ } );
438
+ // Update the sort order to match the DEFAULT_TEMPLATE_SLUGS order.
439
+ enhancedMissingDefaultTemplateTypes?.sort( ( template1, template2 ) => {
440
+ return (
441
+ DEFAULT_TEMPLATE_SLUGS.indexOf( template1.slug ) -
442
+ DEFAULT_TEMPLATE_SLUGS.indexOf( template2.slug )
443
+ );
444
+ } );
445
+ const missingTemplates = [
446
+ ...enhancedMissingDefaultTemplateTypes,
447
+ ...usePostTypeArchiveMenuItems(),
448
+ ...postTypesMenuItems,
449
+ ...taxonomiesMenuItems,
450
+ ];
451
+ return missingTemplates;
452
+ }
453
+
454
+ export default memo( NewTemplate );