@wordpress/edit-site 5.28.5 → 5.28.6

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 (33) hide show
  1. package/build/components/block-editor/use-site-editor-settings.js +1 -1
  2. package/build/components/block-editor/use-site-editor-settings.js.map +1 -1
  3. package/build/components/global-styles/font-library-modal/context.js +15 -4
  4. package/build/components/global-styles/font-library-modal/context.js.map +1 -1
  5. package/build/components/global-styles/font-library-modal/font-collection.js +2 -2
  6. package/build/components/global-styles/font-library-modal/font-collection.js.map +1 -1
  7. package/build/components/global-styles/font-library-modal/upload-fonts.js +44 -10
  8. package/build/components/global-styles/font-library-modal/upload-fonts.js.map +1 -1
  9. package/build/components/global-styles/font-library-modal/utils/index.js +28 -1
  10. package/build/components/global-styles/font-library-modal/utils/index.js.map +1 -1
  11. package/build/components/keyboard-shortcuts/global.js +17 -3
  12. package/build/components/keyboard-shortcuts/global.js.map +1 -1
  13. package/build-module/components/block-editor/use-site-editor-settings.js +1 -1
  14. package/build-module/components/block-editor/use-site-editor-settings.js.map +1 -1
  15. package/build-module/components/global-styles/font-library-modal/context.js +17 -6
  16. package/build-module/components/global-styles/font-library-modal/context.js.map +1 -1
  17. package/build-module/components/global-styles/font-library-modal/font-collection.js +2 -2
  18. package/build-module/components/global-styles/font-library-modal/font-collection.js.map +1 -1
  19. package/build-module/components/global-styles/font-library-modal/upload-fonts.js +44 -10
  20. package/build-module/components/global-styles/font-library-modal/upload-fonts.js.map +1 -1
  21. package/build-module/components/global-styles/font-library-modal/utils/index.js +27 -1
  22. package/build-module/components/global-styles/font-library-modal/utils/index.js.map +1 -1
  23. package/build-module/components/keyboard-shortcuts/global.js +17 -3
  24. package/build-module/components/keyboard-shortcuts/global.js.map +1 -1
  25. package/build-style/style-rtl.css +12 -10
  26. package/build-style/style.css +12 -10
  27. package/package.json +19 -19
  28. package/src/components/block-editor/use-site-editor-settings.js +0 -2
  29. package/src/components/global-styles/font-library-modal/context.js +39 -12
  30. package/src/components/global-styles/font-library-modal/font-collection.js +2 -2
  31. package/src/components/global-styles/font-library-modal/upload-fonts.js +53 -8
  32. package/src/components/global-styles/font-library-modal/utils/index.js +35 -1
  33. package/src/components/keyboard-shortcuts/global.js +16 -4
@@ -495,12 +495,13 @@ body.is-fullscreen-mode .interface-interface-skeleton {
495
495
  box-sizing: border-box;
496
496
  scroll-padding-bottom: 64px;
497
497
  }
498
- .dataviews-wrapper > div {
499
- min-height: 100%;
500
- }
501
498
 
502
499
  .dataviews-filters__view-actions {
503
500
  padding: 12px 32px 0;
501
+ margin-bottom: 12px;
502
+ flex-shrink: 0;
503
+ position: sticky;
504
+ left: 0;
504
505
  }
505
506
  .dataviews-filters__view-actions .components-search-control .components-base-control__field {
506
507
  max-width: 240px;
@@ -515,15 +516,14 @@ body.is-fullscreen-mode .interface-interface-skeleton {
515
516
  }
516
517
 
517
518
  .dataviews-pagination {
518
- margin-top: auto;
519
519
  position: sticky;
520
520
  bottom: 0;
521
- background-color: rgba(255, 255, 255, 0.8);
522
- -webkit-backdrop-filter: blur(6px);
523
- backdrop-filter: blur(6px);
521
+ left: 0;
522
+ background-color: #fff;
524
523
  padding: 12px 32px;
525
524
  border-top: 1px solid #f0f0f0;
526
525
  color: #757575;
526
+ flex-shrink: 0;
527
527
  }
528
528
 
529
529
  .dataviews-pagination__page-selection {
@@ -619,17 +619,19 @@ body.is-fullscreen-mode .interface-interface-skeleton {
619
619
  .dataviews-view-table tr.is-selected:hover {
620
620
  background-color: rgba(var(--wp-admin-theme-color--rgb), 0.08);
621
621
  }
622
+ .dataviews-view-table thead {
623
+ position: sticky;
624
+ inset-block-start: 0;
625
+ z-index: 1;
626
+ }
622
627
  .dataviews-view-table thead tr {
623
628
  border: 0;
624
629
  }
625
630
  .dataviews-view-table thead th {
626
- position: sticky;
627
- top: -1px;
628
631
  background-color: #fff;
629
632
  box-shadow: inset 0 -1px 0 #f0f0f0;
630
633
  padding-top: 8px;
631
634
  padding-bottom: 8px;
632
- z-index: 1;
633
635
  font-size: 11px;
634
636
  text-transform: uppercase;
635
637
  font-weight: 500;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/edit-site",
3
- "version": "5.28.5",
3
+ "version": "5.28.6",
4
4
  "description": "Edit Site Page module for WordPress.",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -30,42 +30,42 @@
30
30
  "@wordpress/a11y": "^3.51.1",
31
31
  "@wordpress/api-fetch": "^6.48.1",
32
32
  "@wordpress/blob": "^3.51.1",
33
- "@wordpress/block-editor": "^12.19.5",
34
- "@wordpress/block-library": "^8.28.5",
35
- "@wordpress/blocks": "^12.28.5",
36
- "@wordpress/commands": "^0.22.4",
37
- "@wordpress/components": "^26.0.4",
33
+ "@wordpress/block-editor": "^12.19.6",
34
+ "@wordpress/block-library": "^8.28.6",
35
+ "@wordpress/blocks": "^12.28.6",
36
+ "@wordpress/commands": "^0.22.5",
37
+ "@wordpress/components": "^26.0.5",
38
38
  "@wordpress/compose": "^6.28.1",
39
- "@wordpress/core-commands": "^0.20.5",
40
- "@wordpress/core-data": "^6.28.5",
39
+ "@wordpress/core-commands": "^0.20.6",
40
+ "@wordpress/core-data": "^6.28.6",
41
41
  "@wordpress/data": "^9.21.1",
42
- "@wordpress/dataviews": "^0.5.5",
42
+ "@wordpress/dataviews": "^0.5.6",
43
43
  "@wordpress/date": "^4.51.1",
44
44
  "@wordpress/deprecated": "^3.51.1",
45
45
  "@wordpress/dom": "^3.51.1",
46
- "@wordpress/editor": "^13.28.5",
46
+ "@wordpress/editor": "^13.28.6",
47
47
  "@wordpress/element": "^5.28.1",
48
48
  "@wordpress/escape-html": "^2.51.1",
49
49
  "@wordpress/hooks": "^3.51.1",
50
50
  "@wordpress/html-entities": "^3.51.1",
51
51
  "@wordpress/i18n": "^4.51.1",
52
- "@wordpress/icons": "^9.42.3",
53
- "@wordpress/interface": "^5.28.4",
52
+ "@wordpress/icons": "^9.42.4",
53
+ "@wordpress/interface": "^5.28.5",
54
54
  "@wordpress/keyboard-shortcuts": "^4.28.1",
55
55
  "@wordpress/keycodes": "^3.51.1",
56
- "@wordpress/media-utils": "^4.42.1",
56
+ "@wordpress/media-utils": "^4.42.2",
57
57
  "@wordpress/notices": "^4.19.1",
58
- "@wordpress/patterns": "^1.12.5",
59
- "@wordpress/plugins": "^6.19.4",
60
- "@wordpress/preferences": "^3.28.4",
58
+ "@wordpress/patterns": "^1.12.6",
59
+ "@wordpress/plugins": "^6.19.5",
60
+ "@wordpress/preferences": "^3.28.5",
61
61
  "@wordpress/primitives": "^3.49.1",
62
62
  "@wordpress/private-apis": "^0.33.1",
63
- "@wordpress/reusable-blocks": "^4.28.5",
63
+ "@wordpress/reusable-blocks": "^4.28.6",
64
64
  "@wordpress/router": "^0.20.1",
65
65
  "@wordpress/style-engine": "^1.34.1",
66
66
  "@wordpress/url": "^3.52.1",
67
67
  "@wordpress/viewport": "^5.28.1",
68
- "@wordpress/widgets": "^3.28.5",
68
+ "@wordpress/widgets": "^3.28.6",
69
69
  "@wordpress/wordcount": "^3.51.1",
70
70
  "change-case": "^4.1.2",
71
71
  "classnames": "^2.3.1",
@@ -85,5 +85,5 @@
85
85
  "publishConfig": {
86
86
  "access": "public"
87
87
  },
88
- "gitHead": "4927ea437069f9aed12f696df294a79bd8e12fd5"
88
+ "gitHead": "7d2a00f1998a0a696694802725e523628f994cfc"
89
89
  }
@@ -101,8 +101,6 @@ function useNavigateToPreviousEntityRecord() {
101
101
  ( location.params.postId &&
102
102
  FOCUSABLE_ENTITIES.includes( location.params.postType ) );
103
103
  const didComeFromEditorCanvas =
104
- previousLocation?.params.postId &&
105
- previousLocation?.params.postType &&
106
104
  previousLocation?.params.canvas === 'edit';
107
105
  const showBackButton = isFocusMode && didComeFromEditorCanvas;
108
106
  return showBackButton ? () => history.back() : undefined;
@@ -9,7 +9,7 @@ import {
9
9
  useEntityRecords,
10
10
  store as coreStore,
11
11
  } from '@wordpress/core-data';
12
- import { __, sprintf } from '@wordpress/i18n';
12
+ import { __ } from '@wordpress/i18n';
13
13
 
14
14
  /**
15
15
  * Internal dependencies
@@ -27,6 +27,7 @@ import {
27
27
  setUIValuesNeeded,
28
28
  mergeFontFamilies,
29
29
  loadFontFaceInBrowser,
30
+ unloadFontFaceInBrowser,
30
31
  getDisplaySrcFromFontFace,
31
32
  makeFontFacesFormData,
32
33
  makeFontFamilyFormData,
@@ -283,6 +284,14 @@ function FontLibraryProvider( { children } ) {
283
284
  );
284
285
  }
285
286
 
287
+ installationErrors = installationErrors.reduce(
288
+ ( unique, item ) =>
289
+ unique.includes( item.message )
290
+ ? unique
291
+ : [ ...unique, item.message ],
292
+ []
293
+ );
294
+
286
295
  if ( fontFamiliesToActivate.length > 0 ) {
287
296
  // Activate the font family (add the font family to the global styles).
288
297
  activateCustomFontFamilies( fontFamiliesToActivate );
@@ -299,18 +308,13 @@ function FontLibraryProvider( { children } ) {
299
308
  }
300
309
 
301
310
  if ( installationErrors.length > 0 ) {
302
- throw new Error(
303
- sprintf(
304
- /* translators: %s: Specific error message returned from server. */
305
- __( 'There were some errors installing fonts. %s' ),
306
- installationErrors.reduce(
307
- ( errorMessageCollection, error ) => {
308
- return `${ errorMessageCollection } ${ error.message }`;
309
- },
310
- ''
311
- )
312
- )
311
+ const installError = new Error(
312
+ __( 'There was an error installing fonts.' )
313
313
  );
314
+
315
+ installError.installationErrors = installationErrors;
316
+
317
+ throw installError;
314
318
  }
315
319
  } finally {
316
320
  setIsInstalling( false );
@@ -363,6 +367,12 @@ function FontLibraryProvider( { children } ) {
363
367
  ...fontFamilies,
364
368
  [ font.source ]: newCustomFonts,
365
369
  } );
370
+
371
+ if ( font.fontFace ) {
372
+ font.fontFace.forEach( ( face ) => {
373
+ unloadFontFaceInBrowser( face, 'all' );
374
+ } );
375
+ }
366
376
  };
367
377
 
368
378
  const activateCustomFontFamilies = ( fontsToAdd ) => {
@@ -398,6 +408,23 @@ function FontLibraryProvider( { children } ) {
398
408
  ...fontFamilies,
399
409
  [ font.source ]: newFonts,
400
410
  } );
411
+
412
+ const isFaceActivated = isFontActivated(
413
+ font.slug,
414
+ face.fontStyle,
415
+ face.fontWeight,
416
+ font.source
417
+ );
418
+
419
+ if ( isFaceActivated ) {
420
+ loadFontFaceInBrowser(
421
+ face,
422
+ getDisplaySrcFromFontFace( face.src ),
423
+ 'all'
424
+ );
425
+ } else {
426
+ unloadFontFaceInBrowser( face, 'all' );
427
+ }
401
428
  };
402
429
 
403
430
  const loadFontFaceAsset = async ( fontFace ) => {
@@ -458,13 +458,13 @@ function FontCollection( { slug } ) {
458
458
  sprintf(
459
459
  // translators: %s: Total number of pages.
460
460
  _x(
461
- 'Page <CurrenPageControl /> of %s',
461
+ 'Page <CurrentPageControl /> of %s',
462
462
  'paging'
463
463
  ),
464
464
  totalPages
465
465
  ),
466
466
  {
467
- CurrenPageControl: (
467
+ CurrentPageControl: (
468
468
  <SelectControl
469
469
  aria-label={ __( 'Current page' ) }
470
470
  value={ page }
@@ -45,29 +45,48 @@ function UploadFonts() {
45
45
  * @param {Array} files The files to be filtered
46
46
  * @return {void}
47
47
  */
48
- const handleFilesUpload = ( files ) => {
48
+ const handleFilesUpload = async ( files ) => {
49
49
  setNotice( null );
50
50
  setIsUploading( true );
51
51
  const uniqueFilenames = new Set();
52
52
  const selectedFiles = [ ...files ];
53
- const allowedFiles = selectedFiles.filter( ( file ) => {
53
+ let hasInvalidFiles = false;
54
+
55
+ // Use map to create a promise for each file check, then filter with Promise.all.
56
+ const checkFilesPromises = selectedFiles.map( async ( file ) => {
57
+ const isFont = await isFontFile( file );
58
+ if ( ! isFont ) {
59
+ hasInvalidFiles = true;
60
+ return null; // Return null for invalid files.
61
+ }
62
+ // Check for duplicates
54
63
  if ( uniqueFilenames.has( file.name ) ) {
55
- return false; // Discard duplicates
64
+ return null; // Return null for duplicates.
56
65
  }
57
- // Eliminates files that are not allowed
66
+ // Check if the file extension is allowed.
58
67
  const fileExtension = file.name.split( '.' ).pop().toLowerCase();
59
68
  if ( ALLOWED_FILE_EXTENSIONS.includes( fileExtension ) ) {
60
69
  uniqueFilenames.add( file.name );
61
- return true; // Keep file if the extension is allowed
70
+ return file; // Return the file if it passes all checks.
62
71
  }
63
- return false; // Discard file extension not allowed
72
+ return null; // Return null for disallowed file extensions.
64
73
  } );
74
+
75
+ // Filter out the nulls after all promises have resolved.
76
+ const allowedFiles = ( await Promise.all( checkFilesPromises ) ).filter(
77
+ ( file ) => null !== file
78
+ );
79
+
65
80
  if ( allowedFiles.length > 0 ) {
66
81
  loadFiles( allowedFiles );
67
82
  } else {
83
+ const message = hasInvalidFiles
84
+ ? __( 'Sorry, you are not allowed to upload this file type.' )
85
+ : __( 'No fonts found to install.' );
86
+
68
87
  setNotice( {
69
88
  type: 'error',
70
- message: __( 'No fonts found to install.' ),
89
+ message,
71
90
  } );
72
91
  setIsUploading( false );
73
92
  }
@@ -94,6 +113,23 @@ function UploadFonts() {
94
113
  handleInstall( fontFacesLoaded );
95
114
  };
96
115
 
116
+ /**
117
+ * Checks if a file is a valid Font file.
118
+ *
119
+ * @param {File} file The file to be checked.
120
+ * @return {boolean} Whether the file is a valid font file.
121
+ */
122
+ async function isFontFile( file ) {
123
+ const font = new Font( 'Uploaded Font' );
124
+ try {
125
+ const buffer = await readFileAsArrayBuffer( file );
126
+ await font.fromDataBuffer( buffer, 'font' );
127
+ return true;
128
+ } catch ( error ) {
129
+ return false;
130
+ }
131
+ }
132
+
97
133
  // Create a function to read the file as array buffer
98
134
  async function readFileAsArrayBuffer( file ) {
99
135
  return new Promise( ( resolve, reject ) => {
@@ -154,6 +190,7 @@ function UploadFonts() {
154
190
  setNotice( {
155
191
  type: 'error',
156
192
  message: error.message,
193
+ errors: error?.installationErrors,
157
194
  } );
158
195
  }
159
196
 
@@ -167,9 +204,17 @@ function UploadFonts() {
167
204
  { notice && (
168
205
  <Notice
169
206
  status={ notice.type }
207
+ __unstableHTML
170
208
  onRemove={ () => setNotice( null ) }
171
209
  >
172
210
  { notice.message }
211
+ { notice.errors && (
212
+ <ul>
213
+ { notice.errors.map( ( error, index ) => (
214
+ <li key={ index }>{ error }</li>
215
+ ) ) }
216
+ </ul>
217
+ ) }
173
218
  </Notice>
174
219
  ) }
175
220
  { isUploading && (
@@ -199,7 +244,7 @@ function UploadFonts() {
199
244
  <Spacer margin={ 2 } />
200
245
  <Text className="font-library-modal__upload-area__text">
201
246
  { __(
202
- 'Uploaded fonts appear in your library and can be used in your theme. Supported formats: .tff, .otf, .woff, and .woff2.'
247
+ 'Uploaded fonts appear in your library and can be used in your theme. Supported formats: .ttf, .otf, .woff, and .woff2.'
203
248
  ) }
204
249
  </Text>
205
250
  </VStack>
@@ -121,6 +121,40 @@ export async function loadFontFaceInBrowser( fontFace, source, addTo = 'all' ) {
121
121
  }
122
122
  }
123
123
 
124
+ /*
125
+ * Unloads the font face and remove it from the browser.
126
+ * It also removes it from the iframe document.
127
+ *
128
+ * Note that Font faces that were added to the set using the CSS @font-face rule
129
+ * remain connected to the corresponding CSS, and cannot be deleted.
130
+ *
131
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet/delete.
132
+ */
133
+ export function unloadFontFaceInBrowser( fontFace, removeFrom = 'all' ) {
134
+ const unloadFontFace = ( fonts ) => {
135
+ fonts.forEach( ( f ) => {
136
+ if (
137
+ f.family === formatFontFaceName( fontFace.fontFamily ) &&
138
+ f.weight === fontFace.fontWeight &&
139
+ f.style === fontFace.fontStyle
140
+ ) {
141
+ fonts.delete( f );
142
+ }
143
+ } );
144
+ };
145
+
146
+ if ( removeFrom === 'document' || removeFrom === 'all' ) {
147
+ unloadFontFace( document.fonts );
148
+ }
149
+
150
+ if ( removeFrom === 'iframe' || removeFrom === 'all' ) {
151
+ const iframeDocument = document.querySelector(
152
+ 'iframe[name="editor-canvas"]'
153
+ ).contentDocument;
154
+ unloadFontFace( iframeDocument.fonts );
155
+ }
156
+ }
157
+
124
158
  /**
125
159
  * Retrieves the display source from a font face src.
126
160
  *
@@ -225,7 +259,7 @@ export async function batchInstallFontFaces( fontFamilyId, fontFacesData ) {
225
259
  // Handle network errors or other fetch-related errors
226
260
  results.errors.push( {
227
261
  data: fontFacesData[ index ],
228
- message: `Fetch error: ${ result.reason.message }`,
262
+ message: result.reason.message,
229
263
  } );
230
264
  }
231
265
  } );
@@ -4,29 +4,41 @@
4
4
  import { useShortcut } from '@wordpress/keyboard-shortcuts';
5
5
  import { useDispatch, useSelect } from '@wordpress/data';
6
6
  import { store as coreStore } from '@wordpress/core-data';
7
+ import { store as editorStore } from '@wordpress/editor';
7
8
 
8
9
  /**
9
10
  * Internal dependencies
10
11
  */
11
12
  import { store as editSiteStore } from '../../store';
13
+ import { unlock } from '../../lock-unlock';
12
14
 
13
15
  function KeyboardShortcutsGlobal() {
14
16
  const { __experimentalGetDirtyEntityRecords, isSavingEntityRecord } =
15
17
  useSelect( coreStore );
18
+ const { hasNonPostEntityChanges } = useSelect( editorStore );
19
+ const { getCanvasMode } = unlock( useSelect( editSiteStore ) );
16
20
  const { setIsSaveViewOpened } = useDispatch( editSiteStore );
17
21
 
18
22
  useShortcut( 'core/edit-site/save', ( event ) => {
19
23
  event.preventDefault();
20
24
 
21
25
  const dirtyEntityRecords = __experimentalGetDirtyEntityRecords();
22
- const isDirty = !! dirtyEntityRecords.length;
26
+ const hasDirtyEntities = !! dirtyEntityRecords.length;
23
27
  const isSaving = dirtyEntityRecords.some( ( record ) =>
24
28
  isSavingEntityRecord( record.kind, record.name, record.key )
25
29
  );
26
-
27
- if ( ! isSaving && isDirty ) {
28
- setIsSaveViewOpened( true );
30
+ const _hasNonPostEntityChanges = hasNonPostEntityChanges();
31
+ const isViewMode = getCanvasMode() === 'view';
32
+ if (
33
+ ( ! hasDirtyEntities || ! _hasNonPostEntityChanges || isSaving ) &&
34
+ ! isViewMode
35
+ ) {
36
+ return;
29
37
  }
38
+ // At this point, we know that there are dirty entities, other than
39
+ // the edited post, and we're not in the process of saving, so open
40
+ // save view.
41
+ setIsSaveViewOpened( true );
30
42
  } );
31
43
 
32
44
  return null;