@wordpress/edit-site 5.28.4 → 5.28.5

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 (39) hide show
  1. package/build/components/global-styles/font-library-modal/context.js +51 -58
  2. package/build/components/global-styles/font-library-modal/context.js.map +1 -1
  3. package/build/components/global-styles/font-library-modal/font-collection.js +4 -4
  4. package/build/components/global-styles/font-library-modal/font-collection.js.map +1 -1
  5. package/build/components/global-styles/font-library-modal/index.js +17 -4
  6. package/build/components/global-styles/font-library-modal/index.js.map +1 -1
  7. package/build/components/global-styles/font-library-modal/installed-fonts.js +10 -1
  8. package/build/components/global-styles/font-library-modal/installed-fonts.js.map +1 -1
  9. package/build/components/global-styles/font-library-modal/upload-fonts.js +2 -10
  10. package/build/components/global-styles/font-library-modal/upload-fonts.js.map +1 -1
  11. package/build/components/global-styles/font-library-modal/utils/index.js +11 -4
  12. package/build/components/global-styles/font-library-modal/utils/index.js.map +1 -1
  13. package/build/components/resizable-frame/index.js +2 -1
  14. package/build/components/resizable-frame/index.js.map +1 -1
  15. package/build-module/components/global-styles/font-library-modal/context.js +51 -58
  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 +4 -4
  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/index.js +17 -4
  20. package/build-module/components/global-styles/font-library-modal/index.js.map +1 -1
  21. package/build-module/components/global-styles/font-library-modal/installed-fonts.js +10 -1
  22. package/build-module/components/global-styles/font-library-modal/installed-fonts.js.map +1 -1
  23. package/build-module/components/global-styles/font-library-modal/upload-fonts.js +2 -10
  24. package/build-module/components/global-styles/font-library-modal/upload-fonts.js.map +1 -1
  25. package/build-module/components/global-styles/font-library-modal/utils/index.js +11 -4
  26. package/build-module/components/global-styles/font-library-modal/utils/index.js.map +1 -1
  27. package/build-module/components/resizable-frame/index.js +2 -1
  28. package/build-module/components/resizable-frame/index.js.map +1 -1
  29. package/build-style/style-rtl.css +8 -1
  30. package/build-style/style.css +8 -1
  31. package/package.json +18 -18
  32. package/src/components/global-styles/font-library-modal/context.js +92 -104
  33. package/src/components/global-styles/font-library-modal/font-collection.js +8 -6
  34. package/src/components/global-styles/font-library-modal/index.js +21 -14
  35. package/src/components/global-styles/font-library-modal/installed-fonts.js +18 -1
  36. package/src/components/global-styles/font-library-modal/upload-fonts.js +3 -13
  37. package/src/components/global-styles/font-library-modal/utils/index.js +10 -4
  38. package/src/components/global-styles/font-library-modal/utils/test/getDisplaySrcFromFontFace.spec.js +7 -18
  39. package/src/components/resizable-frame/index.js +1 -0
@@ -162,16 +162,6 @@ function FontLibraryProvider( { children } ) {
162
162
  // Demo
163
163
  const [ loadedFontUrls ] = useState( new Set() );
164
164
 
165
- // Theme data
166
- const { site, currentTheme } = useSelect( ( select ) => {
167
- return {
168
- site: select( coreStore ).getSite(),
169
- currentTheme: select( coreStore ).getCurrentTheme(),
170
- };
171
- } );
172
- const themeUrl =
173
- site?.url + '/wp-content/themes/' + currentTheme?.stylesheet;
174
-
175
165
  const getAvailableFontsOutline = ( availableFontFamilies ) => {
176
166
  const outline = availableFontFamilies.reduce( ( acc, font ) => {
177
167
  const availableFontFaces =
@@ -210,113 +200,115 @@ function FontLibraryProvider( { children } ) {
210
200
  return getActivatedFontsOutline( source )[ slug ] || [];
211
201
  };
212
202
 
213
- async function installFont( fontFamilyToInstall ) {
203
+ async function installFonts( fontFamiliesToInstall ) {
214
204
  setIsInstalling( true );
215
205
  try {
216
- // Get the font family if it already exists.
217
- let installedFontFamily = await fetchGetFontFamilyBySlug(
218
- fontFamilyToInstall.slug
219
- );
206
+ const fontFamiliesToActivate = [];
207
+ let installationErrors = [];
208
+
209
+ for ( const fontFamilyToInstall of fontFamiliesToInstall ) {
210
+ let isANewFontFamily = false;
220
211
 
221
- // Otherwise create it.
222
- if ( ! installedFontFamily ) {
223
- // Prepare font family form data to install.
224
- installedFontFamily = await fetchInstallFontFamily(
225
- makeFontFamilyFormData( fontFamilyToInstall )
212
+ // Get the font family if it already exists.
213
+ let installedFontFamily = await fetchGetFontFamilyBySlug(
214
+ fontFamilyToInstall.slug
226
215
  );
227
- }
228
216
 
229
- // Collect font faces that have already been installed (to be activated later)
230
- const alreadyInstalledFontFaces =
231
- installedFontFamily.fontFace && fontFamilyToInstall.fontFace
232
- ? installedFontFamily.fontFace.filter(
217
+ // Otherwise create it.
218
+ if ( ! installedFontFamily ) {
219
+ isANewFontFamily = true;
220
+ // Prepare font family form data to install.
221
+ installedFontFamily = await fetchInstallFontFamily(
222
+ makeFontFamilyFormData( fontFamilyToInstall )
223
+ );
224
+ }
225
+
226
+ // Collect font faces that have already been installed (to be activated later)
227
+ const alreadyInstalledFontFaces =
228
+ installedFontFamily.fontFace && fontFamilyToInstall.fontFace
229
+ ? installedFontFamily.fontFace.filter(
230
+ ( fontFaceToInstall ) =>
231
+ checkFontFaceInstalled(
232
+ fontFaceToInstall,
233
+ fontFamilyToInstall.fontFace
234
+ )
235
+ )
236
+ : [];
237
+
238
+ // Filter out Font Faces that have already been installed (so that they are not re-installed)
239
+ if (
240
+ installedFontFamily.fontFace &&
241
+ fontFamilyToInstall.fontFace
242
+ ) {
243
+ fontFamilyToInstall.fontFace =
244
+ fontFamilyToInstall.fontFace.filter(
233
245
  ( fontFaceToInstall ) =>
234
- checkFontFaceInstalled(
246
+ ! checkFontFaceInstalled(
235
247
  fontFaceToInstall,
236
- fontFamilyToInstall.fontFace
248
+ installedFontFamily.fontFace
237
249
  )
238
- )
239
- : [];
240
-
241
- // Filter out Font Faces that have already been installed (so that they are not re-installed)
242
- if (
243
- installedFontFamily.fontFace &&
244
- fontFamilyToInstall.fontFace
245
- ) {
246
- fontFamilyToInstall.fontFace =
247
- fontFamilyToInstall.fontFace.filter(
248
- ( fontFaceToInstall ) =>
249
- ! checkFontFaceInstalled(
250
- fontFaceToInstall,
251
- installedFontFamily.fontFace
252
- )
250
+ );
251
+ }
252
+
253
+ // Install the fonts (upload the font files to the server and create the post in the database).
254
+ let sucessfullyInstalledFontFaces = [];
255
+ let unsucessfullyInstalledFontFaces = [];
256
+ if ( fontFamilyToInstall?.fontFace?.length > 0 ) {
257
+ const response = await batchInstallFontFaces(
258
+ installedFontFamily.id,
259
+ makeFontFacesFormData( fontFamilyToInstall )
253
260
  );
254
- }
255
-
256
- // Install the fonts (upload the font files to the server and create the post in the database).
257
- let sucessfullyInstalledFontFaces = [];
258
- let unsucessfullyInstalledFontFaces = [];
259
- if ( fontFamilyToInstall?.fontFace?.length > 0 ) {
260
- const response = await batchInstallFontFaces(
261
- installedFontFamily.id,
262
- makeFontFacesFormData( fontFamilyToInstall )
261
+ sucessfullyInstalledFontFaces = response?.successes;
262
+ unsucessfullyInstalledFontFaces = response?.errors;
263
+ }
264
+
265
+ // Use the sucessfully installed font faces
266
+ // As well as any font faces that were already installed (those will be activated)
267
+ if (
268
+ sucessfullyInstalledFontFaces?.length > 0 ||
269
+ alreadyInstalledFontFaces?.length > 0
270
+ ) {
271
+ fontFamilyToInstall.fontFace = [
272
+ ...sucessfullyInstalledFontFaces,
273
+ ...alreadyInstalledFontFaces,
274
+ ];
275
+ fontFamiliesToActivate.push( fontFamilyToInstall );
276
+ } else if ( isANewFontFamily ) {
277
+ // If the font family is new, delete it to avoid having font families without font faces.
278
+ await fetchUninstallFontFamily( installedFontFamily.id );
279
+ }
280
+
281
+ installationErrors = installationErrors.concat(
282
+ unsucessfullyInstalledFontFaces
263
283
  );
264
- sucessfullyInstalledFontFaces = response?.successes;
265
- unsucessfullyInstalledFontFaces = response?.errors;
266
284
  }
267
285
 
268
- const detailedErrorMessage = unsucessfullyInstalledFontFaces.reduce(
269
- ( errorMessageCollection, error ) => {
270
- return `${ errorMessageCollection } ${ error.message }`;
271
- },
272
- ''
273
- );
286
+ if ( fontFamiliesToActivate.length > 0 ) {
287
+ // Activate the font family (add the font family to the global styles).
288
+ activateCustomFontFamilies( fontFamiliesToActivate );
274
289
 
275
- // If there were no successes and nothing already installed then we don't need to activate anything and can bounce now.
276
- if (
277
- fontFamilyToInstall?.fontFace?.length > 0 &&
278
- sucessfullyInstalledFontFaces.length === 0 &&
279
- alreadyInstalledFontFaces.length === 0
280
- ) {
281
- throw new Error(
282
- sprintf(
283
- /* translators: %s: Specific error message returned from server. */
284
- __( 'No font faces were installed. %s' ),
285
- detailedErrorMessage
286
- )
290
+ // Save the global styles to the database.
291
+ await saveSpecifiedEntityEdits(
292
+ 'root',
293
+ 'globalStyles',
294
+ globalStylesId,
295
+ [ 'settings.typography.fontFamilies' ]
287
296
  );
288
- }
289
297
 
290
- // Use the sucessfully installed font faces
291
- // As well as any font faces that were already installed (those will be activated)
292
- if (
293
- sucessfullyInstalledFontFaces?.length > 0 ||
294
- alreadyInstalledFontFaces?.length > 0
295
- ) {
296
- fontFamilyToInstall.fontFace = [
297
- ...sucessfullyInstalledFontFaces,
298
- ...alreadyInstalledFontFaces,
299
- ];
298
+ refreshLibrary();
300
299
  }
301
300
 
302
- // Activate the font family (add the font family to the global styles).
303
- activateCustomFontFamilies( [ fontFamilyToInstall ] );
304
-
305
- // Save the global styles to the database.
306
- saveSpecifiedEntityEdits( 'root', 'globalStyles', globalStylesId, [
307
- 'settings.typography.fontFamilies',
308
- ] );
309
-
310
- refreshLibrary();
311
-
312
- if ( unsucessfullyInstalledFontFaces.length > 0 ) {
301
+ if ( installationErrors.length > 0 ) {
313
302
  throw new Error(
314
303
  sprintf(
315
304
  /* translators: %s: Specific error message returned from server. */
316
- __(
317
- 'Some font faces were installed. There were some errors. %s'
318
- ),
319
- detailedErrorMessage
305
+ __( 'There were some errors installing fonts. %s' ),
306
+ installationErrors.reduce(
307
+ ( errorMessageCollection, error ) => {
308
+ return `${ errorMessageCollection } ${ error.message }`;
309
+ },
310
+ ''
311
+ )
320
312
  )
321
313
  );
322
314
  }
@@ -375,14 +367,10 @@ function FontLibraryProvider( { children } ) {
375
367
 
376
368
  const activateCustomFontFamilies = ( fontsToAdd ) => {
377
369
  // Merge the existing custom fonts with the new fonts.
378
- const newCustomFonts = mergeFontFamilies(
379
- fontFamilies?.custom,
380
- fontsToAdd
381
- );
382
370
  // Activate the fonts by set the new custom fonts array.
383
371
  setFontFamilies( {
384
372
  ...fontFamilies,
385
- custom: newCustomFonts,
373
+ custom: mergeFontFamilies( fontFamilies?.custom, fontsToAdd ),
386
374
  } );
387
375
  // Add custom fonts to the browser.
388
376
  fontsToAdd.forEach( ( font ) => {
@@ -416,7 +404,7 @@ function FontLibraryProvider( { children } ) {
416
404
  // If the font doesn't have a src, don't load it.
417
405
  if ( ! fontFace.src ) return;
418
406
  // Get the src of the font.
419
- const src = getDisplaySrcFromFontFace( fontFace.src, themeUrl );
407
+ const src = getDisplaySrcFromFontFace( fontFace.src );
420
408
  // If the font is already loaded, don't load it again.
421
409
  if ( ! src || loadedFontUrls.has( src ) ) return;
422
410
  // Load the font in the browser.
@@ -467,7 +455,7 @@ function FontLibraryProvider( { children } ) {
467
455
  isFontActivated,
468
456
  getFontFacesActivated,
469
457
  loadFontFaceAsset,
470
- installFont,
458
+ installFonts,
471
459
  uninstallFontFamily,
472
460
  toggleActivateFont,
473
461
  getAvailableFontsOutline,
@@ -77,7 +77,7 @@ function FontCollection( { slug } ) {
77
77
  const {
78
78
  collections,
79
79
  getFontCollection,
80
- installFont,
80
+ installFonts,
81
81
  isInstalling,
82
82
  notice,
83
83
  setNotice,
@@ -214,7 +214,7 @@ function FontCollection( { slug } ) {
214
214
  }
215
215
 
216
216
  try {
217
- await installFont( fontFamily );
217
+ await installFonts( [ fontFamily ] );
218
218
  setNotice( {
219
219
  type: 'success',
220
220
  message: __( 'Fonts were installed successfully.' ),
@@ -277,12 +277,14 @@ function FontCollection( { slug } ) {
277
277
  >
278
278
  <NavigatorScreen path="/">
279
279
  <HStack justify="space-between">
280
- <Heading level={ 2 } size={ 13 }>
281
- { selectedCollection.name }
282
- </Heading>
280
+ <VStack>
281
+ <Heading level={ 2 } size={ 13 }>
282
+ { selectedCollection.name }
283
+ </Heading>
284
+ <Text>{ selectedCollection.description }</Text>
285
+ </VStack>
283
286
  <ActionsComponent />
284
287
  </HStack>
285
- <Text>{ selectedCollection.description }</Text>
286
288
  <Spacer margin={ 4 } />
287
289
  <Flex>
288
290
  <FlexItem>
@@ -6,6 +6,8 @@ import {
6
6
  Modal,
7
7
  privateApis as componentsPrivateApis,
8
8
  } from '@wordpress/components';
9
+ import { store as coreStore } from '@wordpress/core-data';
10
+ import { useSelect } from '@wordpress/data';
9
11
  import { useContext } from '@wordpress/element';
10
12
 
11
13
  /**
@@ -19,16 +21,15 @@ import { unlock } from '../../../lock-unlock';
19
21
 
20
22
  const { Tabs } = unlock( componentsPrivateApis );
21
23
 
22
- const DEFAULT_TABS = [
23
- {
24
- id: 'installed-fonts',
25
- title: __( 'Library' ),
26
- },
27
- {
28
- id: 'upload-fonts',
29
- title: __( 'Upload' ),
30
- },
31
- ];
24
+ const DEFAULT_TAB = {
25
+ id: 'installed-fonts',
26
+ title: __( 'Library' ),
27
+ };
28
+
29
+ const UPLOAD_TAB = {
30
+ id: 'upload-fonts',
31
+ title: __( 'Upload' ),
32
+ };
32
33
 
33
34
  const tabsFromCollections = ( collections ) =>
34
35
  collections.map( ( { slug, name } ) => ( {
@@ -44,11 +45,17 @@ function FontLibraryModal( {
44
45
  initialTabId = 'installed-fonts',
45
46
  } ) {
46
47
  const { collections, setNotice } = useContext( FontLibraryContext );
48
+ const canUserCreate = useSelect( ( select ) => {
49
+ const { canUser } = select( coreStore );
50
+ return canUser( 'create', 'font-families' );
51
+ }, [] );
52
+
53
+ const tabs = [ DEFAULT_TAB ];
47
54
 
48
- const tabs = [
49
- ...DEFAULT_TABS,
50
- ...tabsFromCollections( collections || [] ),
51
- ];
55
+ if ( canUserCreate ) {
56
+ tabs.push( UPLOAD_TAB );
57
+ tabs.push( ...tabsFromCollections( collections || [] ) );
58
+ }
52
59
 
53
60
  // Reset notice when new tab is selected.
54
61
  const onSelect = () => {
@@ -18,6 +18,8 @@ import {
18
18
  Spinner,
19
19
  privateApis as componentsPrivateApis,
20
20
  } from '@wordpress/components';
21
+ import { store as coreStore } from '@wordpress/core-data';
22
+ import { useSelect } from '@wordpress/data';
21
23
  import { useContext, useEffect, useState } from '@wordpress/element';
22
24
  import { __, sprintf } from '@wordpress/i18n';
23
25
  import { chevronLeft } from '@wordpress/icons';
@@ -49,9 +51,24 @@ function InstalledFonts() {
49
51
  setNotice,
50
52
  } = useContext( FontLibraryContext );
51
53
  const [ isConfirmDeleteOpen, setIsConfirmDeleteOpen ] = useState( false );
54
+ const customFontFamilyId =
55
+ libraryFontSelected?.source === 'custom' && libraryFontSelected?.id;
56
+
57
+ const canUserDelete = useSelect(
58
+ ( select ) => {
59
+ const { canUser } = select( coreStore );
60
+ return (
61
+ customFontFamilyId &&
62
+ canUser( 'delete', 'font-families', customFontFamilyId )
63
+ );
64
+ },
65
+ [ customFontFamilyId ]
66
+ );
52
67
 
53
68
  const shouldDisplayDeleteButton =
54
- !! libraryFontSelected && libraryFontSelected?.source !== 'theme';
69
+ !! libraryFontSelected &&
70
+ libraryFontSelected?.source !== 'theme' &&
71
+ canUserDelete;
55
72
 
56
73
  const handleUninstallClick = () => {
57
74
  setIsConfirmDeleteOpen( true );
@@ -28,7 +28,8 @@ import { unlock } from '../../../lock-unlock';
28
28
  const { ProgressBar } = unlock( componentsPrivateApis );
29
29
 
30
30
  function UploadFonts() {
31
- const { installFont, notice, setNotice } = useContext( FontLibraryContext );
31
+ const { installFonts, notice, setNotice } =
32
+ useContext( FontLibraryContext );
32
33
  const [ isUploading, setIsUploading ] = useState( false );
33
34
 
34
35
  const handleDropZone = ( files ) => {
@@ -143,19 +144,8 @@ function UploadFonts() {
143
144
  const handleInstall = async ( fontFaces ) => {
144
145
  const fontFamilies = makeFamiliesFromFaces( fontFaces );
145
146
 
146
- if ( fontFamilies.length > 1 ) {
147
- setNotice( {
148
- type: 'error',
149
- message: __(
150
- 'Variants from only one font family can be uploaded at a time.'
151
- ),
152
- } );
153
- setIsUploading( false );
154
- return;
155
- }
156
-
157
147
  try {
158
- await installFont( fontFamilies[ 0 ] );
148
+ await installFonts( fontFamilies );
159
149
  setNotice( {
160
150
  type: 'success',
161
151
  message: __( 'Fonts were installed successfully.' ),
@@ -121,7 +121,13 @@ export async function loadFontFaceInBrowser( fontFace, source, addTo = 'all' ) {
121
121
  }
122
122
  }
123
123
 
124
- export function getDisplaySrcFromFontFace( input, urlPrefix ) {
124
+ /**
125
+ * Retrieves the display source from a font face src.
126
+ *
127
+ * @param {string|string[]} input - The font face src.
128
+ * @return {string|undefined} The display source or undefined if the input is invalid.
129
+ */
130
+ export function getDisplaySrcFromFontFace( input ) {
125
131
  if ( ! input ) {
126
132
  return;
127
133
  }
@@ -132,9 +138,9 @@ export function getDisplaySrcFromFontFace( input, urlPrefix ) {
132
138
  } else {
133
139
  src = input;
134
140
  }
135
- // If it is a theme font, we need to make the url absolute
136
- if ( src.startsWith( 'file:.' ) && urlPrefix ) {
137
- src = src.replace( 'file:.', urlPrefix );
141
+ // It's expected theme fonts will already be loaded in the browser.
142
+ if ( src.startsWith( 'file:.' ) ) {
143
+ return;
138
144
  }
139
145
  if ( ! isUrlEncoded( src ) ) {
140
146
  src = encodeURI( src );
@@ -21,33 +21,22 @@ describe( 'getDisplaySrcFromFontFace', () => {
21
21
  );
22
22
  } );
23
23
 
24
- it( 'makes URL absolute when it starts with file:. and urlPrefix is given', () => {
25
- const input = 'file:./font1';
26
- const urlPrefix = 'http://example.com';
27
- expect( getDisplaySrcFromFontFace( input, urlPrefix ) ).toBe(
28
- 'http://example.com/font1'
29
- );
30
- } );
31
-
32
- it( 'does not modify URL if it does not start with file:.', () => {
33
- const input = [ 'http://some-other-place.com/font1' ];
34
- const urlPrefix = 'http://example.com';
35
- expect( getDisplaySrcFromFontFace( input, urlPrefix ) ).toBe(
36
- 'http://some-other-place.com/font1'
37
- );
24
+ it( 'return undefined when the url starts with file:', () => {
25
+ const input = 'file:./theme/assets/font1.ttf';
26
+ expect( getDisplaySrcFromFontFace( input ) ).toBe( undefined );
38
27
  } );
39
28
 
40
29
  it( 'encodes the URL if it is not encoded', () => {
41
- const input = 'file:./assets/font one with spaces.ttf';
30
+ const input = 'https://example.org/font one with spaces.ttf';
42
31
  expect( getDisplaySrcFromFontFace( input ) ).toBe(
43
- 'file:./assets/font%20one%20with%20spaces.ttf'
32
+ 'https://example.org/font%20one%20with%20spaces.ttf'
44
33
  );
45
34
  } );
46
35
 
47
36
  it( 'does not encode the URL if it is already encoded', () => {
48
- const input = 'file:./font%20one';
37
+ const input = 'https://example.org/fonts/font%20one.ttf';
49
38
  expect( getDisplaySrcFromFontFace( input ) ).toBe(
50
- 'file:./font%20one'
39
+ 'https://example.org/fonts/font%20one.ttf'
51
40
  );
52
41
  } );
53
42
  } );
@@ -300,6 +300,7 @@ function ResizableFrame( {
300
300
  className={ classnames( 'edit-site-resizable-frame__inner', {
301
301
  'is-resizing': isResizing,
302
302
  } ) }
303
+ showHandle={ false } // Do not show the default handle, as we're using a custom one.
303
304
  >
304
305
  <motion.div
305
306
  className="edit-site-resizable-frame__inner-content"