@wordpress/edit-site 5.27.1 → 5.27.2

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 (109) hide show
  1. package/build/components/block-editor/back-button.js +2 -3
  2. package/build/components/block-editor/back-button.js.map +1 -1
  3. package/build/components/block-editor/site-editor-canvas.js +8 -2
  4. package/build/components/block-editor/site-editor-canvas.js.map +1 -1
  5. package/build/components/block-editor/use-post-link-props.js +5 -1
  6. package/build/components/block-editor/use-post-link-props.js.map +1 -1
  7. package/build/components/editor/index.js +7 -7
  8. package/build/components/editor/index.js.map +1 -1
  9. package/build/components/global-styles/font-library-modal/context.js +14 -0
  10. package/build/components/global-styles/font-library-modal/context.js.map +1 -1
  11. package/build/components/global-styles/font-library-modal/font-collection.js +17 -33
  12. package/build/components/global-styles/font-library-modal/font-collection.js.map +1 -1
  13. package/build/components/global-styles/font-library-modal/font-demo.js +1 -1
  14. package/build/components/global-styles/font-library-modal/font-demo.js.map +1 -1
  15. package/build/components/global-styles/font-library-modal/google-fonts-confirm-dialog.js +1 -1
  16. package/build/components/global-styles/font-library-modal/google-fonts-confirm-dialog.js.map +1 -1
  17. package/build/components/global-styles/font-library-modal/index.js +10 -3
  18. package/build/components/global-styles/font-library-modal/index.js.map +1 -1
  19. package/build/components/global-styles/font-library-modal/installed-fonts.js +8 -21
  20. package/build/components/global-styles/font-library-modal/installed-fonts.js.map +1 -1
  21. package/build/components/global-styles/font-library-modal/library-font-variant.js +2 -2
  22. package/build/components/global-styles/font-library-modal/library-font-variant.js.map +1 -1
  23. package/build/components/global-styles/font-library-modal/resolvers.js +1 -1
  24. package/build/components/global-styles/font-library-modal/resolvers.js.map +1 -1
  25. package/build/components/global-styles/font-library-modal/tab-panel-layout.js +9 -1
  26. package/build/components/global-styles/font-library-modal/tab-panel-layout.js.map +1 -1
  27. package/build/components/global-styles/font-library-modal/upload-fonts.js +160 -4
  28. package/build/components/global-styles/font-library-modal/upload-fonts.js.map +1 -1
  29. package/build/components/global-styles/font-library-modal/utils/index.js +33 -31
  30. package/build/components/global-styles/font-library-modal/utils/index.js.map +1 -1
  31. package/build/components/routes/link.js +1 -1
  32. package/build/components/routes/link.js.map +1 -1
  33. package/build/components/sidebar-edit-mode/index.js +1 -2
  34. package/build/components/sidebar-edit-mode/index.js.map +1 -1
  35. package/build/components/sidebar-edit-mode/settings-header/index.js +7 -36
  36. package/build/components/sidebar-edit-mode/settings-header/index.js.map +1 -1
  37. package/build/components/welcome-guide/template.js +2 -2
  38. package/build/components/welcome-guide/template.js.map +1 -1
  39. package/build/hooks/commands/use-edit-mode-commands.js +16 -6
  40. package/build/hooks/commands/use-edit-mode-commands.js.map +1 -1
  41. package/build-module/components/block-editor/back-button.js +2 -3
  42. package/build-module/components/block-editor/back-button.js.map +1 -1
  43. package/build-module/components/block-editor/site-editor-canvas.js +8 -2
  44. package/build-module/components/block-editor/site-editor-canvas.js.map +1 -1
  45. package/build-module/components/block-editor/use-post-link-props.js +5 -1
  46. package/build-module/components/block-editor/use-post-link-props.js.map +1 -1
  47. package/build-module/components/editor/index.js +7 -7
  48. package/build-module/components/editor/index.js.map +1 -1
  49. package/build-module/components/global-styles/font-library-modal/context.js +14 -0
  50. package/build-module/components/global-styles/font-library-modal/context.js.map +1 -1
  51. package/build-module/components/global-styles/font-library-modal/font-collection.js +19 -35
  52. package/build-module/components/global-styles/font-library-modal/font-collection.js.map +1 -1
  53. package/build-module/components/global-styles/font-library-modal/font-demo.js +1 -1
  54. package/build-module/components/global-styles/font-library-modal/font-demo.js.map +1 -1
  55. package/build-module/components/global-styles/font-library-modal/google-fonts-confirm-dialog.js +1 -1
  56. package/build-module/components/global-styles/font-library-modal/google-fonts-confirm-dialog.js.map +1 -1
  57. package/build-module/components/global-styles/font-library-modal/index.js +10 -3
  58. package/build-module/components/global-styles/font-library-modal/index.js.map +1 -1
  59. package/build-module/components/global-styles/font-library-modal/installed-fonts.js +9 -22
  60. package/build-module/components/global-styles/font-library-modal/installed-fonts.js.map +1 -1
  61. package/build-module/components/global-styles/font-library-modal/library-font-variant.js +2 -2
  62. package/build-module/components/global-styles/font-library-modal/library-font-variant.js.map +1 -1
  63. package/build-module/components/global-styles/font-library-modal/resolvers.js +1 -1
  64. package/build-module/components/global-styles/font-library-modal/resolvers.js.map +1 -1
  65. package/build-module/components/global-styles/font-library-modal/tab-panel-layout.js +10 -2
  66. package/build-module/components/global-styles/font-library-modal/tab-panel-layout.js.map +1 -1
  67. package/build-module/components/global-styles/font-library-modal/upload-fonts.js +162 -6
  68. package/build-module/components/global-styles/font-library-modal/upload-fonts.js.map +1 -1
  69. package/build-module/components/global-styles/font-library-modal/utils/index.js +32 -30
  70. package/build-module/components/global-styles/font-library-modal/utils/index.js.map +1 -1
  71. package/build-module/components/routes/link.js +1 -1
  72. package/build-module/components/routes/link.js.map +1 -1
  73. package/build-module/components/sidebar-edit-mode/index.js +1 -2
  74. package/build-module/components/sidebar-edit-mode/index.js.map +1 -1
  75. package/build-module/components/sidebar-edit-mode/settings-header/index.js +7 -36
  76. package/build-module/components/sidebar-edit-mode/settings-header/index.js.map +1 -1
  77. package/build-module/components/welcome-guide/template.js +2 -2
  78. package/build-module/components/welcome-guide/template.js.map +1 -1
  79. package/build-module/hooks/commands/use-edit-mode-commands.js +16 -6
  80. package/build-module/hooks/commands/use-edit-mode-commands.js.map +1 -1
  81. package/build-style/style-rtl.css +4 -6
  82. package/build-style/style.css +4 -6
  83. package/package.json +11 -11
  84. package/src/components/block-editor/back-button.js +6 -3
  85. package/src/components/block-editor/site-editor-canvas.js +19 -12
  86. package/src/components/block-editor/use-post-link-props.js +5 -1
  87. package/src/components/editor/index.js +4 -9
  88. package/src/components/global-styles/font-library-modal/context.js +14 -0
  89. package/src/components/global-styles/font-library-modal/font-collection.js +16 -40
  90. package/src/components/global-styles/font-library-modal/font-demo.js +1 -1
  91. package/src/components/global-styles/font-library-modal/google-fonts-confirm-dialog.js +1 -1
  92. package/src/components/global-styles/font-library-modal/index.js +8 -3
  93. package/src/components/global-styles/font-library-modal/installed-fonts.js +12 -31
  94. package/src/components/global-styles/font-library-modal/library-font-variant.js +2 -2
  95. package/src/components/global-styles/font-library-modal/resolvers.js +1 -1
  96. package/src/components/global-styles/font-library-modal/style.scss +0 -10
  97. package/src/components/global-styles/font-library-modal/tab-panel-layout.js +15 -0
  98. package/src/components/global-styles/font-library-modal/upload-fonts.js +201 -6
  99. package/src/components/global-styles/font-library-modal/utils/index.js +44 -38
  100. package/src/components/routes/link.js +1 -1
  101. package/src/components/sidebar-edit-mode/index.js +1 -4
  102. package/src/components/sidebar-edit-mode/settings-header/index.js +12 -34
  103. package/src/components/welcome-guide/template.js +2 -2
  104. package/src/hooks/commands/use-edit-mode-commands.js +26 -14
  105. package/build/components/global-styles/font-library-modal/local-fonts.js +0 -196
  106. package/build/components/global-styles/font-library-modal/local-fonts.js.map +0 -1
  107. package/build-module/components/global-styles/font-library-modal/local-fonts.js +0 -187
  108. package/build-module/components/global-styles/font-library-modal/local-fonts.js.map +0 -1
  109. package/src/components/global-styles/font-library-modal/local-fonts.js +0 -239
@@ -102,13 +102,13 @@ export default function Editor( { isLoading } ) {
102
102
  contextPost,
103
103
  editorMode,
104
104
  canvasMode,
105
- renderingMode,
106
105
  blockEditorMode,
107
106
  isRightSidebarOpen,
108
107
  isInserterOpen,
109
108
  isListViewOpen,
110
109
  showIconLabels,
111
110
  showBlockBreadcrumbs,
111
+ postTypeLabel,
112
112
  } = useSelect( ( select ) => {
113
113
  const { get } = select( preferencesStore );
114
114
  const { getEditedPostContext, getEditorMode, getCanvasMode } = unlock(
@@ -117,7 +117,7 @@ export default function Editor( { isLoading } ) {
117
117
  const { __unstableGetEditorMode } = select( blockEditorStore );
118
118
  const { getActiveComplementaryArea } = select( interfaceStore );
119
119
  const { getEntityRecord } = select( coreDataStore );
120
- const { getRenderingMode, isInserterOpened, isListViewOpened } =
120
+ const { isInserterOpened, isListViewOpened, getPostTypeLabel } =
121
121
  select( editorStore );
122
122
  const _context = getEditedPostContext();
123
123
 
@@ -134,7 +134,6 @@ export default function Editor( { isLoading } ) {
134
134
  : undefined,
135
135
  editorMode: getEditorMode(),
136
136
  canvasMode: getCanvasMode(),
137
- renderingMode: getRenderingMode(),
138
137
  blockEditorMode: __unstableGetEditorMode(),
139
138
  isInserterOpen: isInserterOpened(),
140
139
  isListViewOpen: isListViewOpened(),
@@ -143,6 +142,7 @@ export default function Editor( { isLoading } ) {
143
142
  ),
144
143
  showBlockBreadcrumbs: get( 'core', 'showBlockBreadcrumbs' ),
145
144
  showIconLabels: get( 'core', 'showIconLabels' ),
145
+ postTypeLabel: getPostTypeLabel(),
146
146
  };
147
147
  }, [] );
148
148
 
@@ -267,12 +267,7 @@ export default function Editor( { isLoading } ) {
267
267
  footer={
268
268
  shouldShowBlockBreadcrumbs && (
269
269
  <BlockBreadcrumb
270
- rootLabelText={
271
- postWithTemplate &&
272
- renderingMode !== 'template-only'
273
- ? __( 'Page' )
274
- : __( 'Template' )
275
- }
270
+ rootLabelText={ postTypeLabel }
276
271
  />
277
272
  )
278
273
  }
@@ -55,11 +55,21 @@ function FontLibraryProvider( { children } ) {
55
55
 
56
56
  const [ isInstalling, setIsInstalling ] = useState( false );
57
57
  const [ refreshKey, setRefreshKey ] = useState( 0 );
58
+ const [ notice, setNotice ] = useState( null );
58
59
 
59
60
  const refreshLibrary = () => {
60
61
  setRefreshKey( Date.now() );
61
62
  };
62
63
 
64
+ // Reset notice on dismiss.
65
+ useEffect( () => {
66
+ if ( notice ) {
67
+ notice.onRemove = () => {
68
+ setNotice( null );
69
+ };
70
+ }
71
+ }, [ notice, setNotice ] );
72
+
63
73
  const {
64
74
  records: libraryPosts = [],
65
75
  isResolving: isResolvingLibrary,
@@ -134,6 +144,8 @@ function FontLibraryProvider( { children } ) {
134
144
  }, [ modalTabOpen ] );
135
145
 
136
146
  const handleSetLibraryFontSelected = ( font ) => {
147
+ setNotice( null );
148
+
137
149
  // If font is null, reset the selected font
138
150
  if ( ! font ) {
139
151
  setLibraryFontSelected( null );
@@ -471,6 +483,8 @@ function FontLibraryProvider( { children } ) {
471
483
  modalTabOpen,
472
484
  toggleModal,
473
485
  refreshLibrary,
486
+ notice,
487
+ setNotice,
474
488
  saveFontFamilies,
475
489
  fontFamiliesHasChanges,
476
490
  isResolvingLibrary,
@@ -12,7 +12,6 @@ import {
12
12
  FlexItem,
13
13
  Flex,
14
14
  Button,
15
- Notice,
16
15
  } from '@wordpress/components';
17
16
  import { debounce } from '@wordpress/compose';
18
17
  import { __ } from '@wordpress/i18n';
@@ -30,31 +29,30 @@ import CollectionFontDetails from './collection-font-details';
30
29
  import { toggleFont } from './utils/toggleFont';
31
30
  import { getFontsOutline } from './utils/fonts-outline';
32
31
  import GoogleFontsConfirmDialog from './google-fonts-confirm-dialog';
33
- import { downloadFontFaceAsset } from './utils';
32
+ import { downloadFontFaceAssets } from './utils';
34
33
 
35
34
  const DEFAULT_CATEGORY = {
36
35
  slug: 'all',
37
36
  name: __( 'All' ),
38
37
  };
39
38
  function FontCollection( { slug } ) {
40
- const requiresPermission = slug === 'default-font-collection';
39
+ const requiresPermission = slug === 'google-fonts';
41
40
 
42
41
  const getGoogleFontsPermissionFromStorage = () => {
43
42
  return (
44
43
  window.localStorage.getItem(
45
- 'wp-font-library-default-font-collection-permission'
44
+ 'wp-font-library-google-fonts-permission'
46
45
  ) === 'true'
47
46
  );
48
47
  };
49
48
 
50
- const [ notice, setNotice ] = useState( null );
51
49
  const [ selectedFont, setSelectedFont ] = useState( null );
52
50
  const [ fontsToInstall, setFontsToInstall ] = useState( [] );
53
51
  const [ filters, setFilters ] = useState( {} );
54
52
  const [ renderConfirmDialog, setRenderConfirmDialog ] = useState(
55
53
  requiresPermission && ! getGoogleFontsPermissionFromStorage()
56
54
  );
57
- const { collections, getFontCollection, installFont } =
55
+ const { collections, getFontCollection, installFont, notice, setNotice } =
58
56
  useContext( FontLibraryContext );
59
57
  const selectedCollection = collections.find(
60
58
  ( collection ) => collection.slug === slug
@@ -77,36 +75,27 @@ function FontCollection( { slug } ) {
77
75
  await getFontCollection( slug );
78
76
  resetFilters();
79
77
  } catch ( e ) {
80
- setNotice( {
81
- type: 'error',
82
- message: e?.message,
83
- duration: 0, // Don't auto-hide.
84
- } );
78
+ if ( ! notice ) {
79
+ setNotice( {
80
+ type: 'error',
81
+ message: e?.message,
82
+ } );
83
+ }
85
84
  }
86
85
  };
87
86
  fetchFontCollection();
88
- }, [ slug, getFontCollection ] );
87
+ }, [ slug, getFontCollection, setNotice, notice ] );
89
88
 
90
89
  useEffect( () => {
91
90
  setSelectedFont( null );
92
91
  setNotice( null );
93
- }, [ slug ] );
92
+ }, [ slug, setNotice ] );
94
93
 
95
94
  useEffect( () => {
96
95
  // If the selected fonts change, reset the selected fonts to install
97
96
  setFontsToInstall( [] );
98
97
  }, [ selectedFont ] );
99
98
 
100
- // Reset notice after 5 seconds
101
- useEffect( () => {
102
- if ( notice && notice?.duration !== 0 ) {
103
- const timeout = setTimeout( () => {
104
- setNotice( null );
105
- }, notice.duration ?? 5000 );
106
- return () => clearTimeout( timeout );
107
- }
108
- }, [ notice ] );
109
-
110
99
  const collectionFonts = useMemo(
111
100
  () => selectedCollection?.font_families ?? [],
112
101
  [ selectedCollection ]
@@ -154,6 +143,8 @@ function FontCollection( { slug } ) {
154
143
  };
155
144
 
156
145
  const handleInstall = async () => {
146
+ setNotice( null );
147
+
157
148
  const fontFamily = fontsToInstall[ 0 ];
158
149
 
159
150
  try {
@@ -161,7 +152,7 @@ function FontCollection( { slug } ) {
161
152
  await Promise.all(
162
153
  fontFamily.fontFace.map( async ( fontFace ) => {
163
154
  if ( fontFace.src ) {
164
- fontFace.file = await downloadFontFaceAsset(
155
+ fontFace.file = await downloadFontFaceAssets(
165
156
  fontFace.src
166
157
  );
167
158
  }
@@ -205,6 +196,7 @@ function FontCollection( { slug } ) {
205
196
  ? selectedCollection.description
206
197
  : __( 'Select font variants to install.' )
207
198
  }
199
+ notice={ notice }
208
200
  handleBack={ !! selectedFont && handleUnselectFont }
209
201
  footer={
210
202
  fontsToInstall.length > 0 && (
@@ -219,22 +211,6 @@ function FontCollection( { slug } ) {
219
211
  </>
220
212
  ) }
221
213
 
222
- { notice && (
223
- <>
224
- <FlexItem>
225
- <Spacer margin={ 2 } />
226
- <Notice
227
- isDismissible={ false }
228
- status={ notice.type }
229
- className="font-library-modal__font-collection__notice"
230
- >
231
- { notice.message }
232
- </Notice>
233
- </FlexItem>
234
- <Spacer margin={ 2 } />
235
- </>
236
- ) }
237
-
238
214
  { ! renderConfirmDialog && ! selectedFont && (
239
215
  <Flex>
240
216
  <FlexItem>
@@ -62,7 +62,7 @@ function FontFaceDemo( { customPreviewUrl, fontFace, text, style = {} } ) {
62
62
  }
63
63
  };
64
64
  loadAsset();
65
- }, [ fontFace, isIntersecting, loadFontFaceAsset ] );
65
+ }, [ fontFace, isIntersecting, loadFontFaceAsset, isPreviewImage ] );
66
66
 
67
67
  return (
68
68
  <div ref={ ref }>
@@ -14,7 +14,7 @@ function GoogleFontsConfirmDialog() {
14
14
  const handleConfirm = () => {
15
15
  // eslint-disable-next-line no-undef
16
16
  window.localStorage.setItem(
17
- 'wp-font-library-default-font-collection-permission',
17
+ 'wp-font-library-google-fonts-permission',
18
18
  'true'
19
19
  );
20
20
  window.dispatchEvent( new Event( 'storage' ) );
@@ -34,7 +34,7 @@ const tabsFromCollections = ( collections ) =>
34
34
  collections.map( ( { slug, name } ) => ( {
35
35
  id: slug,
36
36
  title:
37
- collections.length === 1 && slug === 'default-font-collection'
37
+ collections.length === 1 && slug === 'google-fonts'
38
38
  ? __( 'Install Fonts' )
39
39
  : name,
40
40
  } ) );
@@ -43,13 +43,18 @@ function FontLibraryModal( {
43
43
  onRequestClose,
44
44
  initialTabId = 'installed-fonts',
45
45
  } ) {
46
- const { collections } = useContext( FontLibraryContext );
46
+ const { collections, setNotice } = useContext( FontLibraryContext );
47
47
 
48
48
  const tabs = [
49
49
  ...DEFAULT_TABS,
50
50
  ...tabsFromCollections( collections || [] ),
51
51
  ];
52
52
 
53
+ // Reset notice when new tab is selected.
54
+ const onSelect = () => {
55
+ setNotice( null );
56
+ };
57
+
53
58
  return (
54
59
  <Modal
55
60
  title={ __( 'Fonts' ) }
@@ -58,7 +63,7 @@ function FontLibraryModal( {
58
63
  className="font-library-modal"
59
64
  >
60
65
  <div className="font-library-modal__tabs">
61
- <Tabs initialTabId={ initialTabId }>
66
+ <Tabs initialTabId={ initialTabId } onSelect={ onSelect }>
62
67
  <Tabs.TabList>
63
68
  { tabs.map( ( { id, title } ) => (
64
69
  <Tabs.Tab key={ id } tabId={ id }>
@@ -9,7 +9,6 @@ import {
9
9
  __experimentalSpacer as Spacer,
10
10
  Button,
11
11
  Spinner,
12
- Notice,
13
12
  FlexItem,
14
13
  } from '@wordpress/components';
15
14
 
@@ -34,6 +33,8 @@ function InstalledFonts() {
34
33
  refreshLibrary,
35
34
  uninstallFontFamily,
36
35
  isResolvingLibrary,
36
+ notice,
37
+ setNotice,
37
38
  } = useContext( FontLibraryContext );
38
39
  const [ isConfirmDeleteOpen, setIsConfirmDeleteOpen ] = useState( false );
39
40
 
@@ -45,9 +46,9 @@ function InstalledFonts() {
45
46
  handleSetLibraryFontSelected( font );
46
47
  };
47
48
 
48
- const [ notice, setNotice ] = useState( null );
49
-
50
49
  const handleConfirmUninstall = async () => {
50
+ setNotice( null );
51
+
51
52
  try {
52
53
  await uninstallFontFamily( libraryFontSelected );
53
54
  setNotice( {
@@ -91,20 +92,11 @@ function InstalledFonts() {
91
92
  // eslint-disable-next-line react-hooks/exhaustive-deps
92
93
  }, [] );
93
94
 
94
- // Reset notice after 5 seconds
95
- useEffect( () => {
96
- if ( notice ) {
97
- const timeout = setTimeout( () => {
98
- setNotice( null );
99
- }, 5000 );
100
- return () => clearTimeout( timeout );
101
- }
102
- }, [ notice ] );
103
-
104
95
  return (
105
96
  <TabPanelLayout
106
97
  title={ libraryFontSelected?.name || '' }
107
98
  description={ tabDescription }
99
+ notice={ notice }
108
100
  handleBack={ !! libraryFontSelected && handleUnselectFont }
109
101
  footer={
110
102
  <Footer
@@ -120,28 +112,17 @@ function InstalledFonts() {
120
112
  handleCancelUninstall={ handleCancelUninstall }
121
113
  />
122
114
 
123
- { notice && (
124
- <>
125
- <FlexItem>
126
- <Spacer margin={ 2 } />
127
- <Notice
128
- isDismissible={ false }
129
- status={ notice.type }
130
- className="font-library-modal__font-collection__notice"
131
- >
132
- { notice.message }
133
- </Notice>
134
- </FlexItem>
135
- <Spacer margin={ 4 } />
136
- </>
137
- ) }
138
-
139
115
  { ! libraryFontSelected && (
140
116
  <>
141
- { isResolvingLibrary && <Spinner /> }
117
+ { isResolvingLibrary && (
118
+ <FlexItem>
119
+ <Spacer margin={ 2 } />
120
+ <Spinner />
121
+ <Spacer margin={ 2 } />
122
+ </FlexItem>
123
+ ) }
142
124
  { baseCustomFonts.length > 0 && (
143
125
  <>
144
- <Spacer margin={ 2 } />
145
126
  <FontsGrid>
146
127
  { baseCustomFonts.map( ( font ) => (
147
128
  <LibraryFontCard
@@ -20,7 +20,7 @@ function LibraryFontVariant( { face, font } ) {
20
20
  const { isFontActivated, toggleActivateFont } =
21
21
  useContext( FontLibraryContext );
22
22
 
23
- const isIstalled =
23
+ const isInstalled =
24
24
  font?.fontFace?.length > 0
25
25
  ? isFontActivated(
26
26
  font.slug,
@@ -52,7 +52,7 @@ function LibraryFontVariant( { face, font } ) {
52
52
  <Flex justify="space-between" align="center" gap="1rem">
53
53
  <FontFaceDemo fontFace={ face } text={ displayName } />
54
54
  <CheckboxControl
55
- checked={ isIstalled }
55
+ checked={ isInstalled }
56
56
  onChange={ handleToggleActivation }
57
57
  __nextHasNoMarginBottom={ true }
58
58
  id={ checkboxId }
@@ -63,7 +63,7 @@ export async function fetchUninstallFontFamily( fontFamilyId ) {
63
63
 
64
64
  export async function fetchFontCollections() {
65
65
  const config = {
66
- path: FONT_COLLECTIONS_URL,
66
+ path: `${ FONT_COLLECTIONS_URL }?_fields=slug,name,description`,
67
67
  method: 'GET',
68
68
  };
69
69
  return await apiFetch( config );
@@ -25,11 +25,6 @@
25
25
  }
26
26
 
27
27
  .font-library-modal__tabpanel-layout {
28
-
29
- .font-library-modal__tabpanel-layout__main {
30
- padding-bottom: $grid-unit-80;
31
- }
32
-
33
28
  .font-library-modal__tabpanel-layout__footer {
34
29
  border-top: 1px solid $gray-300;
35
30
  margin: 0 #{$grid-unit-40 * -1} #{$grid-unit-40 * -1};
@@ -39,7 +34,6 @@
39
34
  width: 100%;
40
35
  background-color: $white;
41
36
  }
42
-
43
37
  }
44
38
 
45
39
  .font-library-modal__fonts-grid {
@@ -107,10 +101,6 @@ button.font-library-modal__upload-area {
107
101
  .font-library-modal__upload-area__text {
108
102
  color: $gray-700;
109
103
  }
110
-
111
- .font-library-modal__upload-area__notice {
112
- margin: 0;
113
- }
114
104
  }
115
105
 
116
106
  .font-library-modal__font-variant_demo-wrapper {
@@ -8,12 +8,15 @@ import {
8
8
  __experimentalSpacer as Spacer,
9
9
  __experimentalHStack as HStack,
10
10
  Button,
11
+ Notice,
12
+ FlexBlock,
11
13
  } from '@wordpress/components';
12
14
  import { chevronLeft } from '@wordpress/icons';
13
15
 
14
16
  function TabPanelLayout( {
15
17
  title,
16
18
  description,
19
+ notice,
17
20
  handleBack,
18
21
  children,
19
22
  footer,
@@ -43,6 +46,18 @@ function TabPanelLayout( {
43
46
  ) }
44
47
  </HStack>
45
48
  { description && <Text>{ description }</Text> }
49
+ { notice && (
50
+ <FlexBlock>
51
+ <Spacer margin={ 1 } />
52
+ <Notice
53
+ status={ notice.type }
54
+ onRemove={ notice.onRemove }
55
+ >
56
+ { notice.message }
57
+ </Notice>
58
+ <Spacer margin={ 1 } />
59
+ </FlexBlock>
60
+ ) }
46
61
  </VStack>
47
62
  <div className="font-library-modal__tabpanel-layout__main">
48
63
  { children }
@@ -1,19 +1,214 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import { __experimentalSpacer as Spacer } from '@wordpress/components';
4
+ import { __, sprintf } from '@wordpress/i18n';
5
+ import {
6
+ Button,
7
+ DropZone,
8
+ __experimentalSpacer as Spacer,
9
+ __experimentalText as Text,
10
+ __experimentalVStack as VStack,
11
+ FormFileUpload,
12
+ FlexItem,
13
+ privateApis as componentsPrivateApis,
14
+ } from '@wordpress/components';
15
+ import { useContext, useState } from '@wordpress/element';
5
16
 
6
17
  /**
7
18
  * Internal dependencies
8
19
  */
9
- import LocalFonts from './local-fonts';
20
+ import { ALLOWED_FILE_EXTENSIONS } from './utils/constants';
21
+ import { FontLibraryContext } from './context';
22
+ import { Font } from '../../../../lib/lib-font.browser';
23
+ import makeFamiliesFromFaces from './utils/make-families-from-faces';
24
+ import { loadFontFaceInBrowser } from './utils';
25
+ import TabPanelLayout from './tab-panel-layout';
26
+ import { unlock } from '../../../lock-unlock';
27
+
28
+ const { ProgressBar } = unlock( componentsPrivateApis );
10
29
 
11
30
  function UploadFonts() {
31
+ const { installFont, notice, setNotice } = useContext( FontLibraryContext );
32
+ const [ isUploading, setIsUploading ] = useState( false );
33
+ const supportedFormats =
34
+ ALLOWED_FILE_EXTENSIONS.slice( 0, -1 )
35
+ .map( ( extension ) => `.${ extension }` )
36
+ .join( ', ' ) +
37
+ ` ${ __( 'and' ) } .${ ALLOWED_FILE_EXTENSIONS.slice( -1 ) }`;
38
+
39
+ const handleDropZone = ( files ) => {
40
+ handleFilesUpload( files );
41
+ };
42
+ const onFilesUpload = ( event ) => {
43
+ handleFilesUpload( event.target.files );
44
+ };
45
+
46
+ /**
47
+ * Filters the selected files to only allow the ones with the allowed extensions
48
+ *
49
+ * @param {Array} files The files to be filtered
50
+ * @return {void}
51
+ */
52
+ const handleFilesUpload = ( files ) => {
53
+ setNotice( null );
54
+ setIsUploading( true );
55
+ const uniqueFilenames = new Set();
56
+ const selectedFiles = [ ...files ];
57
+ const allowedFiles = selectedFiles.filter( ( file ) => {
58
+ if ( uniqueFilenames.has( file.name ) ) {
59
+ return false; // Discard duplicates
60
+ }
61
+ // Eliminates files that are not allowed
62
+ const fileExtension = file.name.split( '.' ).pop().toLowerCase();
63
+ if ( ALLOWED_FILE_EXTENSIONS.includes( fileExtension ) ) {
64
+ uniqueFilenames.add( file.name );
65
+ return true; // Keep file if the extension is allowed
66
+ }
67
+ return false; // Discard file extension not allowed
68
+ } );
69
+ if ( allowedFiles.length > 0 ) {
70
+ loadFiles( allowedFiles );
71
+ }
72
+ };
73
+
74
+ /**
75
+ * Loads the selected files and reads the font metadata
76
+ *
77
+ * @param {Array} files The files to be loaded
78
+ * @return {void}
79
+ */
80
+ const loadFiles = async ( files ) => {
81
+ const fontFacesLoaded = await Promise.all(
82
+ files.map( async ( fontFile ) => {
83
+ const fontFaceData = await getFontFaceMetadata( fontFile );
84
+ await loadFontFaceInBrowser(
85
+ fontFaceData,
86
+ fontFaceData.file,
87
+ 'all'
88
+ );
89
+ return fontFaceData;
90
+ } )
91
+ );
92
+ handleInstall( fontFacesLoaded );
93
+ };
94
+
95
+ // Create a function to read the file as array buffer
96
+ async function readFileAsArrayBuffer( file ) {
97
+ return new Promise( ( resolve, reject ) => {
98
+ const reader = new window.FileReader();
99
+ reader.readAsArrayBuffer( file );
100
+ reader.onload = () => resolve( reader.result );
101
+ reader.onerror = reject;
102
+ } );
103
+ }
104
+
105
+ const getFontFaceMetadata = async ( fontFile ) => {
106
+ const buffer = await readFileAsArrayBuffer( fontFile );
107
+ const fontObj = new Font( 'Uploaded Font' );
108
+ fontObj.fromDataBuffer( buffer, fontFile.name );
109
+ // Assuming that fromDataBuffer triggers onload event and returning a Promise
110
+ const onloadEvent = await new Promise(
111
+ ( resolve ) => ( fontObj.onload = resolve )
112
+ );
113
+ const font = onloadEvent.detail.font;
114
+ const { name } = font.opentype.tables;
115
+ const fontName = name.get( 16 ) || name.get( 1 );
116
+ const isItalic = name.get( 2 ).toLowerCase().includes( 'italic' );
117
+ const fontWeight =
118
+ font.opentype.tables[ 'OS/2' ].usWeightClass || 'normal';
119
+ const isVariable = !! font.opentype.tables.fvar;
120
+ const weightAxis =
121
+ isVariable &&
122
+ font.opentype.tables.fvar.axes.find(
123
+ ( { tag } ) => tag === 'wght'
124
+ );
125
+ const weightRange = weightAxis
126
+ ? `${ weightAxis.minValue } ${ weightAxis.maxValue }`
127
+ : null;
128
+ return {
129
+ file: fontFile,
130
+ fontFamily: fontName,
131
+ fontStyle: isItalic ? 'italic' : 'normal',
132
+ fontWeight: weightRange || fontWeight,
133
+ };
134
+ };
135
+
136
+ /**
137
+ * Creates the font family definition and sends it to the server
138
+ *
139
+ * @param {Array} fontFaces The font faces to be installed
140
+ * @return {void}
141
+ */
142
+ const handleInstall = async ( fontFaces ) => {
143
+ const fontFamilies = makeFamiliesFromFaces( fontFaces );
144
+
145
+ if ( fontFamilies.length > 1 ) {
146
+ setNotice( {
147
+ type: 'error',
148
+ message: __(
149
+ 'Variants from only one font family can be uploaded at a time.'
150
+ ),
151
+ } );
152
+ setIsUploading( false );
153
+ return;
154
+ }
155
+
156
+ try {
157
+ await installFont( fontFamilies[ 0 ] );
158
+ setNotice( {
159
+ type: 'success',
160
+ message: __( 'Fonts were installed successfully.' ),
161
+ } );
162
+ } catch ( error ) {
163
+ setNotice( {
164
+ type: 'error',
165
+ message: error.message,
166
+ } );
167
+ }
168
+
169
+ setIsUploading( false );
170
+ };
171
+
12
172
  return (
13
- <>
14
- <Spacer margin={ 8 } />
15
- <LocalFonts />
16
- </>
173
+ <TabPanelLayout notice={ notice }>
174
+ <DropZone onFilesDrop={ handleDropZone } />
175
+ <VStack className="font-library-modal__local-fonts">
176
+ { isUploading && (
177
+ <FlexItem>
178
+ <div className="font-library-modal__upload-area">
179
+ <ProgressBar />
180
+ </div>
181
+ </FlexItem>
182
+ ) }
183
+ { ! isUploading && (
184
+ <FormFileUpload
185
+ accept={ ALLOWED_FILE_EXTENSIONS.map(
186
+ ( ext ) => `.${ ext }`
187
+ ).join( ',' ) }
188
+ multiple={ true }
189
+ onChange={ onFilesUpload }
190
+ render={ ( { openFileDialog } ) => (
191
+ <Button
192
+ className="font-library-modal__upload-area"
193
+ onClick={ openFileDialog }
194
+ >
195
+ <span>{ __( 'Upload font' ) }</span>
196
+ </Button>
197
+ ) }
198
+ />
199
+ ) }
200
+ <Spacer margin={ 2 } />
201
+ <Text className="font-library-modal__upload-area__text">
202
+ { sprintf(
203
+ /* translators: %s: supported font formats: ex: .ttf, .woff and .woff2 */
204
+ __(
205
+ 'Uploaded fonts appear in your library and can be used in your theme. Supported formats: %s.'
206
+ ),
207
+ supportedFormats
208
+ ) }
209
+ </Text>
210
+ </VStack>
211
+ </TabPanelLayout>
17
212
  );
18
213
  }
19
214