From 6219a6d7005e9e23fc622ac653db57265b660678 Mon Sep 17 00:00:00 2001 From: Matias Benedetto Date: Tue, 3 Sep 2024 17:10:42 -0300 Subject: [PATCH 01/12] add editor font face resolver component --- .../src/components/block-canvas/index.js | 3 + .../components/editor-fonts-resolver/index.js | 63 ++++++++ .../components/editor-fonts-resolver/utils.js | 147 ++++++++++++++++++ 3 files changed, 213 insertions(+) create mode 100644 packages/block-editor/src/components/editor-fonts-resolver/index.js create mode 100644 packages/block-editor/src/components/editor-fonts-resolver/utils.js diff --git a/packages/block-editor/src/components/block-canvas/index.js b/packages/block-editor/src/components/block-canvas/index.js index 9bad3ca22e95f..03f3a4f2eb5f1 100644 --- a/packages/block-editor/src/components/block-canvas/index.js +++ b/packages/block-editor/src/components/block-canvas/index.js @@ -15,6 +15,7 @@ import WritingFlow from '../writing-flow'; import { useMouseMoveTypingReset } from '../observe-typing'; import { useBlockSelectionClearer } from '../block-selection-clearer'; import { useBlockCommands } from '../use-block-commands'; +import EditorFontsResolver from '../editor-fonts-resolver'; // EditorStyles is a memoized component, so avoid passing a new // object reference on each render. @@ -43,6 +44,7 @@ export function ExperimentalBlockCanvas( { __unstableContentRef={ localRef } style={ { height, display: 'flex' } } > + + { children } diff --git a/packages/block-editor/src/components/editor-fonts-resolver/index.js b/packages/block-editor/src/components/editor-fonts-resolver/index.js new file mode 100644 index 0000000000000..17fd238c39a8c --- /dev/null +++ b/packages/block-editor/src/components/editor-fonts-resolver/index.js @@ -0,0 +1,63 @@ +/** + * WordPress dependencies + */ +import { useState, useEffect, useMemo } from '@wordpress/element'; +import { useSelect } from '@wordpress/data'; + +/** + * Internal dependencies + */ +import { useGlobalSetting } from '../global-styles/hooks'; +import { getDisplaySrcFromFontFace, loadFontFaceInBrowser } from './utils'; + +function EditorFontsResolver() { + const [ loadedFontUrls ] = useState( new Set() ); + + const { currentTheme } = useSelect( ( select ) => { + return { + currentTheme: + // Using 'core' as string to avoid circular dependency importing from @wordpress/core-data + select( 'core' ).getCurrentTheme(), // eslint-disable-line @wordpress/data-no-store-string-literals + }; + }, [] ); + + //Get the fonts from merged theme.json settings.fontFamilies + const [ fontFamilies ] = useGlobalSetting( 'typography.fontFamilies' ); + + const fontFaces = useMemo( () => { + return Object.values( fontFamilies ) + .flat() + .map( ( family ) => family.fontFace ) + .filter( Boolean ) + .flat(); + }, [ fontFamilies ] ); + + const loadFontFaceAsset = async ( fontFace ) => { + // If the font doesn't have a src, don't load it. + if ( ! fontFace.src ) { + return; + } + // Get the src of the font. + const src = getDisplaySrcFromFontFace( + fontFace.src, + currentTheme?.stylesheet_uri + ); + // If the font is already loaded, don't load it again. + if ( ! src || loadedFontUrls.has( src ) ) { + return; + } + // Load the font in the browser. + loadFontFaceInBrowser( fontFace, src ); + // Add the font to the loaded fonts list. + loadedFontUrls.add( src ); + }; + + useEffect( () => { + fontFaces.forEach( loadFontFaceAsset ); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ fontFaces ] ); + + return null; +} + +export default EditorFontsResolver; diff --git a/packages/block-editor/src/components/editor-fonts-resolver/utils.js b/packages/block-editor/src/components/editor-fonts-resolver/utils.js new file mode 100644 index 0000000000000..23f22a45cc308 --- /dev/null +++ b/packages/block-editor/src/components/editor-fonts-resolver/utils.js @@ -0,0 +1,147 @@ +/* + * Format the font face name to use in the font-family property of a font face. + * + * The input can be a string with the font face name or a string with multiple font face names separated by commas. + * It removes the leading and trailing quotes from the font face name. + * + * @param {string} input - The font face name. + * @return {string} The formatted font face name. + * + * Example: + * formatFontFaceName("Open Sans") => "Open Sans" + * formatFontFaceName("'Open Sans', sans-serif") => "Open Sans" + * formatFontFaceName(", 'Open Sans', 'Helvetica Neue', sans-serif") => "Open Sans" + */ +export function formatFontFaceName( input ) { + if ( ! input ) { + return ''; + } + + let output = input.trim(); + if ( output.includes( ',' ) ) { + output = output + .split( ',' ) + // finds the first item that is not an empty string. + .find( ( item ) => item.trim() !== '' ) + .trim(); + } + // removes leading and trailing quotes. + output = output.replace( /^["']|["']$/g, '' ); + + // Firefox needs the font name to be wrapped in double quotes meanwhile other browsers don't. + if ( window.navigator.userAgent.toLowerCase().includes( 'firefox' ) ) { + output = `"${ output }"`; + } + return output; +} + +/* + * Loads the font face from a URL and adds it to the browser. + * It also adds it to the iframe document. + */ +export async function loadFontFaceInBrowser( fontFace, source, addTo = 'all' ) { + let dataSource; + + if ( typeof source === 'string' ) { + dataSource = `url(${ source })`; + // eslint-disable-next-line no-undef + } else if ( source instanceof File ) { + dataSource = await source.arrayBuffer(); + } else { + return; + } + + const newFont = new window.FontFace( + formatFontFaceName( fontFace.fontFamily ), + dataSource, + { + style: fontFace.fontStyle, + weight: fontFace.fontWeight, + } + ); + + const loadedFace = await newFont.load(); + + if ( addTo === 'document' || addTo === 'all' ) { + document.fonts.add( loadedFace ); + } + + if ( addTo === 'iframe' || addTo === 'all' ) { + const iframeDocument = document.querySelector( + 'iframe[name="editor-canvas"]' + ).contentDocument; + iframeDocument.fonts.add( loadedFace ); + } +} + +/* + * Unloads the font face and remove it from the browser. + * It also removes it from the iframe document. + * + * Note that Font faces that were added to the set using the CSS @font-face rule + * remain connected to the corresponding CSS, and cannot be deleted. + * + * @see https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet/delete. + */ +export function unloadFontFaceInBrowser( fontFace, removeFrom = 'all' ) { + const unloadFontFace = ( fonts ) => { + fonts.forEach( ( f ) => { + if ( + f.family === formatFontFaceName( fontFace?.fontFamily ) && + f.weight === fontFace?.fontWeight && + f.style === fontFace?.fontStyle + ) { + fonts.delete( f ); + } + } ); + }; + + if ( removeFrom === 'document' || removeFrom === 'all' ) { + unloadFontFace( document.fonts ); + } + + if ( removeFrom === 'iframe' || removeFrom === 'all' ) { + const iframeDocument = document.querySelector( + 'iframe[name="editor-canvas"]' + ).contentDocument; + unloadFontFace( iframeDocument.fonts ); + } +} + +function isUrlEncoded( url ) { + if ( typeof url !== 'string' ) { + return false; + } + return url !== decodeURIComponent( url ); +} + +/* + * Retrieves the display source from a font face src. + * + * @param {string|string[]} fontSrc - The font face src. + * @param {string} baseUrl - The base URL to resolve the src. + * @return {string|undefined} The display source or undefined if the input is invalid. + */ +export function getDisplaySrcFromFontFace( fontSrc, baseUrl ) { + if ( ! fontSrc ) { + return; + } + + let src; + if ( Array.isArray( fontSrc ) ) { + src = fontSrc[ 0 ]; + } else { + src = fontSrc; + } + + if ( ! isUrlEncoded( src ) ) { + src = encodeURI( src ); + } + + // If baseUrl is provided, use it to resolve the src. + if ( src.startsWith( 'file:.' ) ) { + src = baseUrl + '/' + src.replace( 'file:./', '' ); + } + + return src; +} From 1e8ef556d85ee6e3777eb23d74e12bec9321ba94 Mon Sep 17 00:00:00 2001 From: Matias Benedetto Date: Wed, 4 Sep 2024 15:36:02 -0300 Subject: [PATCH 02/12] remove code not needed --- .../components/editor-fonts-resolver/utils.js | 38 +------------------ 1 file changed, 1 insertion(+), 37 deletions(-) diff --git a/packages/block-editor/src/components/editor-fonts-resolver/utils.js b/packages/block-editor/src/components/editor-fonts-resolver/utils.js index 23f22a45cc308..305d9eb616365 100644 --- a/packages/block-editor/src/components/editor-fonts-resolver/utils.js +++ b/packages/block-editor/src/components/editor-fonts-resolver/utils.js @@ -12,7 +12,7 @@ * formatFontFaceName("'Open Sans', sans-serif") => "Open Sans" * formatFontFaceName(", 'Open Sans', 'Helvetica Neue', sans-serif") => "Open Sans" */ -export function formatFontFaceName( input ) { +function formatFontFaceName( input ) { if ( ! input ) { return ''; } @@ -45,8 +45,6 @@ export async function loadFontFaceInBrowser( fontFace, source, addTo = 'all' ) { if ( typeof source === 'string' ) { dataSource = `url(${ source })`; // eslint-disable-next-line no-undef - } else if ( source instanceof File ) { - dataSource = await source.arrayBuffer(); } else { return; } @@ -74,40 +72,6 @@ export async function loadFontFaceInBrowser( fontFace, source, addTo = 'all' ) { } } -/* - * Unloads the font face and remove it from the browser. - * It also removes it from the iframe document. - * - * Note that Font faces that were added to the set using the CSS @font-face rule - * remain connected to the corresponding CSS, and cannot be deleted. - * - * @see https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet/delete. - */ -export function unloadFontFaceInBrowser( fontFace, removeFrom = 'all' ) { - const unloadFontFace = ( fonts ) => { - fonts.forEach( ( f ) => { - if ( - f.family === formatFontFaceName( fontFace?.fontFamily ) && - f.weight === fontFace?.fontWeight && - f.style === fontFace?.fontStyle - ) { - fonts.delete( f ); - } - } ); - }; - - if ( removeFrom === 'document' || removeFrom === 'all' ) { - unloadFontFace( document.fonts ); - } - - if ( removeFrom === 'iframe' || removeFrom === 'all' ) { - const iframeDocument = document.querySelector( - 'iframe[name="editor-canvas"]' - ).contentDocument; - unloadFontFace( iframeDocument.fonts ); - } -} - function isUrlEncoded( url ) { if ( typeof url !== 'string' ) { return false; From 65f37545913412a02bf70d16b8967955d8b1b41d Mon Sep 17 00:00:00 2001 From: Matias Benedetto Date: Thu, 5 Sep 2024 13:12:50 -0300 Subject: [PATCH 03/12] default value when font families aren't defined --- .../block-editor/src/components/editor-fonts-resolver/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/editor-fonts-resolver/index.js b/packages/block-editor/src/components/editor-fonts-resolver/index.js index 17fd238c39a8c..c645e30399e86 100644 --- a/packages/block-editor/src/components/editor-fonts-resolver/index.js +++ b/packages/block-editor/src/components/editor-fonts-resolver/index.js @@ -22,7 +22,7 @@ function EditorFontsResolver() { }, [] ); //Get the fonts from merged theme.json settings.fontFamilies - const [ fontFamilies ] = useGlobalSetting( 'typography.fontFamilies' ); + const [ fontFamilies = [] ] = useGlobalSetting( 'typography.fontFamilies' ); const fontFaces = useMemo( () => { return Object.values( fontFamilies ) From 77f76386a378bdfe6fbf6cfb295e8f048fd52403 Mon Sep 17 00:00:00 2001 From: Matias Benedetto Date: Mon, 9 Sep 2024 09:40:22 -0300 Subject: [PATCH 04/12] comments formatting Co-authored-by: Sarah Norris <1645628+mikachan@users.noreply.github.com> --- .../src/components/editor-fonts-resolver/index.js | 7 ++++--- .../src/components/editor-fonts-resolver/utils.js | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/block-editor/src/components/editor-fonts-resolver/index.js b/packages/block-editor/src/components/editor-fonts-resolver/index.js index c645e30399e86..435da5affc200 100644 --- a/packages/block-editor/src/components/editor-fonts-resolver/index.js +++ b/packages/block-editor/src/components/editor-fonts-resolver/index.js @@ -16,12 +16,13 @@ function EditorFontsResolver() { const { currentTheme } = useSelect( ( select ) => { return { currentTheme: - // Using 'core' as string to avoid circular dependency importing from @wordpress/core-data - select( 'core' ).getCurrentTheme(), // eslint-disable-line @wordpress/data-no-store-string-literals + // Disable Reason: Using 'core' as string to avoid circular dependency importing from @wordpress/core-data. + // eslint-disable-next-line @wordpress/data-no-store-string-literals + select( 'core' ).getCurrentTheme(), }; }, [] ); - //Get the fonts from merged theme.json settings.fontFamilies + // Get the fonts from merged theme.json settings.fontFamilies. const [ fontFamilies = [] ] = useGlobalSetting( 'typography.fontFamilies' ); const fontFaces = useMemo( () => { diff --git a/packages/block-editor/src/components/editor-fonts-resolver/utils.js b/packages/block-editor/src/components/editor-fonts-resolver/utils.js index 305d9eb616365..66e914910f0a6 100644 --- a/packages/block-editor/src/components/editor-fonts-resolver/utils.js +++ b/packages/block-editor/src/components/editor-fonts-resolver/utils.js @@ -21,11 +21,11 @@ function formatFontFaceName( input ) { if ( output.includes( ',' ) ) { output = output .split( ',' ) - // finds the first item that is not an empty string. + // Finds the first item that is not an empty string. .find( ( item ) => item.trim() !== '' ) .trim(); } - // removes leading and trailing quotes. + // Removes leading and trailing quotes. output = output.replace( /^["']|["']$/g, '' ); // Firefox needs the font name to be wrapped in double quotes meanwhile other browsers don't. From 0bed01d59389a691988c926e12bb6ba552d8002f Mon Sep 17 00:00:00 2001 From: Matias Benedetto Date: Mon, 9 Sep 2024 10:27:49 -0300 Subject: [PATCH 05/12] use callback for loadFontFaceAsset --- .../components/editor-fonts-resolver/index.js | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/packages/block-editor/src/components/editor-fonts-resolver/index.js b/packages/block-editor/src/components/editor-fonts-resolver/index.js index 435da5affc200..1ba220f47b1af 100644 --- a/packages/block-editor/src/components/editor-fonts-resolver/index.js +++ b/packages/block-editor/src/components/editor-fonts-resolver/index.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { useState, useEffect, useMemo } from '@wordpress/element'; +import { useState, useEffect, useMemo, useCallback } from '@wordpress/element'; import { useSelect } from '@wordpress/data'; /** @@ -11,7 +11,7 @@ import { useGlobalSetting } from '../global-styles/hooks'; import { getDisplaySrcFromFontFace, loadFontFaceInBrowser } from './utils'; function EditorFontsResolver() { - const [ loadedFontUrls ] = useState( new Set() ); + const [ loadedFontUrls, setLoadedFontUrls ] = useState( new Set() ); const { currentTheme } = useSelect( ( select ) => { return { @@ -33,30 +33,30 @@ function EditorFontsResolver() { .flat(); }, [ fontFamilies ] ); - const loadFontFaceAsset = async ( fontFace ) => { - // If the font doesn't have a src, don't load it. - if ( ! fontFace.src ) { - return; - } - // Get the src of the font. - const src = getDisplaySrcFromFontFace( - fontFace.src, - currentTheme?.stylesheet_uri - ); - // If the font is already loaded, don't load it again. - if ( ! src || loadedFontUrls.has( src ) ) { - return; - } - // Load the font in the browser. - loadFontFaceInBrowser( fontFace, src ); - // Add the font to the loaded fonts list. - loadedFontUrls.add( src ); - }; + const loadFontFaceAsset = useCallback( + async ( fontFace ) => { + if ( ! fontFace.src ) { + return; + } + + const src = getDisplaySrcFromFontFace( + fontFace.src, + currentTheme?.stylesheet_uri + ); + + if ( ! src || loadedFontUrls.has( src ) ) { + return; + } + + loadFontFaceInBrowser( fontFace, src ); + setLoadedFontUrls( ( prevUrls ) => new Set( prevUrls ).add( src ) ); + }, + [ currentTheme, loadedFontUrls ] + ); useEffect( () => { fontFaces.forEach( loadFontFaceAsset ); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ fontFaces ] ); + }, [ fontFaces, loadFontFaceAsset ] ); return null; } From 48bfe657541705df1a6961a0c533b9533fd607e4 Mon Sep 17 00:00:00 2001 From: Matias Benedetto Date: Mon, 9 Sep 2024 10:35:14 -0300 Subject: [PATCH 06/12] improve syntax --- .../src/components/editor-fonts-resolver/utils.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/block-editor/src/components/editor-fonts-resolver/utils.js b/packages/block-editor/src/components/editor-fonts-resolver/utils.js index 66e914910f0a6..f208d66830d31 100644 --- a/packages/block-editor/src/components/editor-fonts-resolver/utils.js +++ b/packages/block-editor/src/components/editor-fonts-resolver/utils.js @@ -40,15 +40,10 @@ function formatFontFaceName( input ) { * It also adds it to the iframe document. */ export async function loadFontFaceInBrowser( fontFace, source, addTo = 'all' ) { - let dataSource; - - if ( typeof source === 'string' ) { - dataSource = `url(${ source })`; - // eslint-disable-next-line no-undef - } else { + if ( typeof source !== 'string' ) { return; } - + const dataSource = `url(${ source })`; const newFont = new window.FontFace( formatFontFaceName( fontFace.fontFamily ), dataSource, From c6a32f9d7381e4f93a3c471de7214023f4984edd Mon Sep 17 00:00:00 2001 From: Matias Benedetto Date: Fri, 13 Sep 2024 16:58:24 -0300 Subject: [PATCH 07/12] Move EditorFontsResolver inside EditorStyles, use fontFamilies data from block-editor store --- .../block-editor/src/components/block-canvas/index.js | 3 --- .../src/components/editor-fonts-resolver/index.js | 10 +++++----- .../block-editor/src/components/editor-styles/index.js | 3 ++- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/block-editor/src/components/block-canvas/index.js b/packages/block-editor/src/components/block-canvas/index.js index 03f3a4f2eb5f1..9bad3ca22e95f 100644 --- a/packages/block-editor/src/components/block-canvas/index.js +++ b/packages/block-editor/src/components/block-canvas/index.js @@ -15,7 +15,6 @@ import WritingFlow from '../writing-flow'; import { useMouseMoveTypingReset } from '../observe-typing'; import { useBlockSelectionClearer } from '../block-selection-clearer'; import { useBlockCommands } from '../use-block-commands'; -import EditorFontsResolver from '../editor-fonts-resolver'; // EditorStyles is a memoized component, so avoid passing a new // object reference on each render. @@ -44,7 +43,6 @@ export function ExperimentalBlockCanvas( { __unstableContentRef={ localRef } style={ { height, display: 'flex' } } > - - { children } diff --git a/packages/block-editor/src/components/editor-fonts-resolver/index.js b/packages/block-editor/src/components/editor-fonts-resolver/index.js index 1ba220f47b1af..97f490e11c40a 100644 --- a/packages/block-editor/src/components/editor-fonts-resolver/index.js +++ b/packages/block-editor/src/components/editor-fonts-resolver/index.js @@ -7,24 +7,24 @@ import { useSelect } from '@wordpress/data'; /** * Internal dependencies */ -import { useGlobalSetting } from '../global-styles/hooks'; import { getDisplaySrcFromFontFace, loadFontFaceInBrowser } from './utils'; +import { store as editorStore } from '../../store'; function EditorFontsResolver() { const [ loadedFontUrls, setLoadedFontUrls ] = useState( new Set() ); - const { currentTheme } = useSelect( ( select ) => { + const { currentTheme, fontFamilies = [] } = useSelect( ( select ) => { return { currentTheme: // Disable Reason: Using 'core' as string to avoid circular dependency importing from @wordpress/core-data. // eslint-disable-next-line @wordpress/data-no-store-string-literals select( 'core' ).getCurrentTheme(), + fontFamilies: + select( editorStore ).getSettings()?.__experimentalFeatures + ?.typography?.fontFamilies, }; }, [] ); - // Get the fonts from merged theme.json settings.fontFamilies. - const [ fontFamilies = [] ] = useGlobalSetting( 'typography.fontFamilies' ); - const fontFaces = useMemo( () => { return Object.values( fontFamilies ) .flat() diff --git a/packages/block-editor/src/components/editor-styles/index.js b/packages/block-editor/src/components/editor-styles/index.js index a59ac310bcd30..5108e8fae7b51 100644 --- a/packages/block-editor/src/components/editor-styles/index.js +++ b/packages/block-editor/src/components/editor-styles/index.js @@ -18,6 +18,7 @@ import { useSelect } from '@wordpress/data'; import transformStyles from '../../utils/transform-styles'; import { store as blockEditorStore } from '../../store'; import { unlock } from '../../lock-unlock'; +import EditorFontsResolver from '../editor-fonts-resolver'; extend( [ namesPlugin, a11yPlugin ] ); @@ -27,7 +28,6 @@ function useDarkThemeBodyClassName( styles, scope ) { if ( ! node ) { return; } - const { ownerDocument } = node; const { defaultView, body } = ownerDocument; const canvas = scope ? ownerDocument.querySelector( scope ) : body; @@ -122,6 +122,7 @@ function EditorStyles( { styles, scope, transformOptions } ) { } } dangerouslySetInnerHTML={ { __html: transformedSvgs } } /> + ); } From 5a9e2d0cc698aec84edf6e3809f8f379a537325c Mon Sep 17 00:00:00 2001 From: Matias Benedetto Date: Fri, 13 Sep 2024 18:15:54 -0300 Subject: [PATCH 08/12] use a ref to reference the current document --- .../src/components/editor-styles/index.js | 88 +++++++++---------- .../index.js | 8 +- .../utils.js | 15 ++-- 3 files changed, 52 insertions(+), 59 deletions(-) rename packages/block-editor/src/components/{editor-fonts-resolver => use-editor-fonts-resolver}/index.js (91%) rename packages/block-editor/src/components/{editor-fonts-resolver => use-editor-fonts-resolver}/utils.js (86%) diff --git a/packages/block-editor/src/components/editor-styles/index.js b/packages/block-editor/src/components/editor-styles/index.js index 5108e8fae7b51..2a52c0de077a3 100644 --- a/packages/block-editor/src/components/editor-styles/index.js +++ b/packages/block-editor/src/components/editor-styles/index.js @@ -9,7 +9,7 @@ import a11yPlugin from 'colord/plugins/a11y'; * WordPress dependencies */ import { SVG } from '@wordpress/components'; -import { useCallback, useMemo, memo } from '@wordpress/element'; +import { useMemo, memo, useRef } from '@wordpress/element'; import { useSelect } from '@wordpress/data'; /** @@ -18,53 +18,49 @@ import { useSelect } from '@wordpress/data'; import transformStyles from '../../utils/transform-styles'; import { store as blockEditorStore } from '../../store'; import { unlock } from '../../lock-unlock'; -import EditorFontsResolver from '../editor-fonts-resolver'; +import useEditorFontsResolver from '../use-editor-fonts-resolver'; extend( [ namesPlugin, a11yPlugin ] ); -function useDarkThemeBodyClassName( styles, scope ) { - return useCallback( - ( node ) => { - if ( ! node ) { - return; - } - const { ownerDocument } = node; - const { defaultView, body } = ownerDocument; - const canvas = scope ? ownerDocument.querySelector( scope ) : body; +function useDarkThemeBodyClassName( scope, ref ) { + if ( ! ref.current ) { + return; + } - let backgroundColor; + const { ownerDocument } = ref.current; + const { defaultView, body } = ownerDocument; + const canvas = scope ? ownerDocument.querySelector( scope ) : body; - if ( ! canvas ) { - // The real .editor-styles-wrapper element might not exist in the - // DOM, so calculate the background color by creating a fake - // wrapper. - const tempCanvas = ownerDocument.createElement( 'div' ); - tempCanvas.classList.add( 'editor-styles-wrapper' ); - body.appendChild( tempCanvas ); + let backgroundColor; - backgroundColor = defaultView - ?.getComputedStyle( tempCanvas, null ) - .getPropertyValue( 'background-color' ); + if ( ! canvas ) { + // The real .editor-styles-wrapper element might not exist in the + // DOM, so calculate the background color by creating a fake + // wrapper. + const tempCanvas = ownerDocument.createElement( 'div' ); + tempCanvas.classList.add( 'editor-styles-wrapper' ); + body.appendChild( tempCanvas ); - body.removeChild( tempCanvas ); - } else { - backgroundColor = defaultView - ?.getComputedStyle( canvas, null ) - .getPropertyValue( 'background-color' ); - } - const colordBackgroundColor = colord( backgroundColor ); - // If background is transparent, it should be treated as light color. - if ( - colordBackgroundColor.luminance() > 0.5 || - colordBackgroundColor.alpha() === 0 - ) { - body.classList.remove( 'is-dark-theme' ); - } else { - body.classList.add( 'is-dark-theme' ); - } - }, - [ styles, scope ] - ); + backgroundColor = defaultView + ?.getComputedStyle( tempCanvas, null ) + .getPropertyValue( 'background-color' ); + + body.removeChild( tempCanvas ); + } else { + backgroundColor = defaultView + ?.getComputedStyle( canvas, null ) + .getPropertyValue( 'background-color' ); + } + const colordBackgroundColor = colord( backgroundColor ); + // If background is transparent, it should be treated as light color. + if ( + colordBackgroundColor.luminance() > 0.5 || + colordBackgroundColor.alpha() === 0 + ) { + body.classList.remove( 'is-dark-theme' ); + } else { + body.classList.add( 'is-dark-theme' ); + } } function EditorStyles( { styles, scope, transformOptions } ) { @@ -98,13 +94,16 @@ function EditorStyles( { styles, scope, transformOptions } ) { ]; }, [ styles, overrides, scope, transformOptions ] ); + const styleRef = useRef( null ); + useDarkThemeBodyClassName( scope, styleRef ); + useEditorFontsResolver( styleRef ); + return ( <> { /* Use an empty style element to have a document reference, but this could be any element. */ } - ) ) } @@ -122,7 +121,6 @@ function EditorStyles( { styles, scope, transformOptions } ) { } } dangerouslySetInnerHTML={ { __html: transformedSvgs } } /> - ); } diff --git a/packages/block-editor/src/components/editor-fonts-resolver/index.js b/packages/block-editor/src/components/use-editor-fonts-resolver/index.js similarity index 91% rename from packages/block-editor/src/components/editor-fonts-resolver/index.js rename to packages/block-editor/src/components/use-editor-fonts-resolver/index.js index 97f490e11c40a..322fd557c129d 100644 --- a/packages/block-editor/src/components/editor-fonts-resolver/index.js +++ b/packages/block-editor/src/components/use-editor-fonts-resolver/index.js @@ -10,7 +10,7 @@ import { useSelect } from '@wordpress/data'; import { getDisplaySrcFromFontFace, loadFontFaceInBrowser } from './utils'; import { store as editorStore } from '../../store'; -function EditorFontsResolver() { +function useEditorFontsResolver( ref ) { const [ loadedFontUrls, setLoadedFontUrls ] = useState( new Set() ); const { currentTheme, fontFamilies = [] } = useSelect( ( select ) => { @@ -48,7 +48,7 @@ function EditorFontsResolver() { return; } - loadFontFaceInBrowser( fontFace, src ); + loadFontFaceInBrowser( fontFace, src, ref.current.ownerDocument ); setLoadedFontUrls( ( prevUrls ) => new Set( prevUrls ).add( src ) ); }, [ currentTheme, loadedFontUrls ] @@ -57,8 +57,6 @@ function EditorFontsResolver() { useEffect( () => { fontFaces.forEach( loadFontFaceAsset ); }, [ fontFaces, loadFontFaceAsset ] ); - - return null; } -export default EditorFontsResolver; +export default useEditorFontsResolver; diff --git a/packages/block-editor/src/components/editor-fonts-resolver/utils.js b/packages/block-editor/src/components/use-editor-fonts-resolver/utils.js similarity index 86% rename from packages/block-editor/src/components/editor-fonts-resolver/utils.js rename to packages/block-editor/src/components/use-editor-fonts-resolver/utils.js index f208d66830d31..467eb4ef82a01 100644 --- a/packages/block-editor/src/components/editor-fonts-resolver/utils.js +++ b/packages/block-editor/src/components/use-editor-fonts-resolver/utils.js @@ -39,7 +39,7 @@ function formatFontFaceName( input ) { * Loads the font face from a URL and adds it to the browser. * It also adds it to the iframe document. */ -export async function loadFontFaceInBrowser( fontFace, source, addTo = 'all' ) { +export async function loadFontFaceInBrowser( fontFace, source, documentRef ) { if ( typeof source !== 'string' ) { return; } @@ -55,15 +55,12 @@ export async function loadFontFaceInBrowser( fontFace, source, addTo = 'all' ) { const loadedFace = await newFont.load(); - if ( addTo === 'document' || addTo === 'all' ) { - document.fonts.add( loadedFace ); - } + // Add the font to the ref document. + documentRef.fonts.add( loadedFace ); - if ( addTo === 'iframe' || addTo === 'all' ) { - const iframeDocument = document.querySelector( - 'iframe[name="editor-canvas"]' - ).contentDocument; - iframeDocument.fonts.add( loadedFace ); + // Add the font to the window document. + if ( documentRef !== window.document ) { + window.document.fonts.add( loadedFace ); } } From 27b5e6441520f7340af434ca1688e7c5aaa1c381 Mon Sep 17 00:00:00 2001 From: Matias Benedetto Date: Mon, 16 Sep 2024 09:49:30 -0300 Subject: [PATCH 09/12] currentTheme default to empty object --- .../src/components/use-editor-fonts-resolver/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/components/use-editor-fonts-resolver/index.js b/packages/block-editor/src/components/use-editor-fonts-resolver/index.js index 322fd557c129d..d6af640274b93 100644 --- a/packages/block-editor/src/components/use-editor-fonts-resolver/index.js +++ b/packages/block-editor/src/components/use-editor-fonts-resolver/index.js @@ -13,12 +13,12 @@ import { store as editorStore } from '../../store'; function useEditorFontsResolver( ref ) { const [ loadedFontUrls, setLoadedFontUrls ] = useState( new Set() ); - const { currentTheme, fontFamilies = [] } = useSelect( ( select ) => { + const { currentTheme = {}, fontFamilies = [] } = useSelect( ( select ) => { return { currentTheme: // Disable Reason: Using 'core' as string to avoid circular dependency importing from @wordpress/core-data. // eslint-disable-next-line @wordpress/data-no-store-string-literals - select( 'core' ).getCurrentTheme(), + select( 'core' )?.getCurrentTheme(), fontFamilies: select( editorStore ).getSettings()?.__experimentalFeatures ?.typography?.fontFamilies, From 463fa952b9cb2170e89398a36625f4195faca6eb Mon Sep 17 00:00:00 2001 From: Matias Benedetto Date: Wed, 18 Sep 2024 09:07:35 -0300 Subject: [PATCH 10/12] revert changes on useDarkThemeBodyClassName, refactor useEditorFontsResolver follwing useDarkThemeBodyClassName pattern --- .../src/components/editor-styles/index.js | 95 ++++++++++--------- .../use-editor-fonts-resolver/index.js | 24 +++-- 2 files changed, 67 insertions(+), 52 deletions(-) diff --git a/packages/block-editor/src/components/editor-styles/index.js b/packages/block-editor/src/components/editor-styles/index.js index 2a52c0de077a3..ca3355596dc5f 100644 --- a/packages/block-editor/src/components/editor-styles/index.js +++ b/packages/block-editor/src/components/editor-styles/index.js @@ -9,7 +9,7 @@ import a11yPlugin from 'colord/plugins/a11y'; * WordPress dependencies */ import { SVG } from '@wordpress/components'; -import { useMemo, memo, useRef } from '@wordpress/element'; +import { useMemo, memo, useCallback } from '@wordpress/element'; import { useSelect } from '@wordpress/data'; /** @@ -22,45 +22,50 @@ import useEditorFontsResolver from '../use-editor-fonts-resolver'; extend( [ namesPlugin, a11yPlugin ] ); -function useDarkThemeBodyClassName( scope, ref ) { - if ( ! ref.current ) { - return; - } - - const { ownerDocument } = ref.current; - const { defaultView, body } = ownerDocument; - const canvas = scope ? ownerDocument.querySelector( scope ) : body; - - let backgroundColor; - - if ( ! canvas ) { - // The real .editor-styles-wrapper element might not exist in the - // DOM, so calculate the background color by creating a fake - // wrapper. - const tempCanvas = ownerDocument.createElement( 'div' ); - tempCanvas.classList.add( 'editor-styles-wrapper' ); - body.appendChild( tempCanvas ); - - backgroundColor = defaultView - ?.getComputedStyle( tempCanvas, null ) - .getPropertyValue( 'background-color' ); - - body.removeChild( tempCanvas ); - } else { - backgroundColor = defaultView - ?.getComputedStyle( canvas, null ) - .getPropertyValue( 'background-color' ); - } - const colordBackgroundColor = colord( backgroundColor ); - // If background is transparent, it should be treated as light color. - if ( - colordBackgroundColor.luminance() > 0.5 || - colordBackgroundColor.alpha() === 0 - ) { - body.classList.remove( 'is-dark-theme' ); - } else { - body.classList.add( 'is-dark-theme' ); - } +function useDarkThemeBodyClassName( styles, scope ) { + return useCallback( + ( node ) => { + if ( ! node ) { + return; + } + + const { ownerDocument } = node; + const { defaultView, body } = ownerDocument; + const canvas = scope ? ownerDocument.querySelector( scope ) : body; + + let backgroundColor; + + if ( ! canvas ) { + // The real .editor-styles-wrapper element might not exist in the + // DOM, so calculate the background color by creating a fake + // wrapper. + const tempCanvas = ownerDocument.createElement( 'div' ); + tempCanvas.classList.add( 'editor-styles-wrapper' ); + body.appendChild( tempCanvas ); + + backgroundColor = defaultView + ?.getComputedStyle( tempCanvas, null ) + .getPropertyValue( 'background-color' ); + + body.removeChild( tempCanvas ); + } else { + backgroundColor = defaultView + ?.getComputedStyle( canvas, null ) + .getPropertyValue( 'background-color' ); + } + const colordBackgroundColor = colord( backgroundColor ); + // If background is transparent, it should be treated as light color. + if ( + colordBackgroundColor.luminance() > 0.5 || + colordBackgroundColor.alpha() === 0 + ) { + body.classList.remove( 'is-dark-theme' ); + } else { + body.classList.add( 'is-dark-theme' ); + } + }, + [ styles, scope ] + ); } function EditorStyles( { styles, scope, transformOptions } ) { @@ -94,15 +99,15 @@ function EditorStyles( { styles, scope, transformOptions } ) { ]; }, [ styles, overrides, scope, transformOptions ] ); - const styleRef = useRef( null ); - useDarkThemeBodyClassName( scope, styleRef ); - useEditorFontsResolver( styleRef ); - return ( <> { /* Use an empty style element to have a document reference, but this could be any element. */ } - diff --git a/packages/block-editor/src/components/use-editor-fonts-resolver/index.js b/packages/block-editor/src/components/use-editor-fonts-resolver/index.js index d6af640274b93..d5cd6ab153590 100644 --- a/packages/block-editor/src/components/use-editor-fonts-resolver/index.js +++ b/packages/block-editor/src/components/use-editor-fonts-resolver/index.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { useState, useEffect, useMemo, useCallback } from '@wordpress/element'; +import { useState, useMemo, useCallback } from '@wordpress/element'; import { useSelect } from '@wordpress/data'; /** @@ -10,7 +10,7 @@ import { useSelect } from '@wordpress/data'; import { getDisplaySrcFromFontFace, loadFontFaceInBrowser } from './utils'; import { store as editorStore } from '../../store'; -function useEditorFontsResolver( ref ) { +function useEditorFontsResolver() { const [ loadedFontUrls, setLoadedFontUrls ] = useState( new Set() ); const { currentTheme = {}, fontFamilies = [] } = useSelect( ( select ) => { @@ -34,7 +34,7 @@ function useEditorFontsResolver( ref ) { }, [ fontFamilies ] ); const loadFontFaceAsset = useCallback( - async ( fontFace ) => { + async ( fontFace, ownerDocument ) => { if ( ! fontFace.src ) { return; } @@ -48,15 +48,25 @@ function useEditorFontsResolver( ref ) { return; } - loadFontFaceInBrowser( fontFace, src, ref.current.ownerDocument ); + loadFontFaceInBrowser( fontFace, src, ownerDocument ); setLoadedFontUrls( ( prevUrls ) => new Set( prevUrls ).add( src ) ); }, [ currentTheme, loadedFontUrls ] ); - useEffect( () => { - fontFaces.forEach( loadFontFaceAsset ); - }, [ fontFaces, loadFontFaceAsset ] ); + return useCallback( + ( node ) => { + if ( ! node ) { + return; + } + + const { ownerDocument } = node; + fontFaces.forEach( ( fontFace ) => + loadFontFaceAsset( fontFace, ownerDocument ) + ); + }, + [ fontFaces, loadFontFaceAsset ] + ); } export default useEditorFontsResolver; From 9bd3378752eb762cd160251c20f3a35b7e494526 Mon Sep 17 00:00:00 2001 From: Matias Benedetto Date: Wed, 18 Sep 2024 09:16:54 -0300 Subject: [PATCH 11/12] revert not needed change --- packages/block-editor/src/components/editor-styles/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/editor-styles/index.js b/packages/block-editor/src/components/editor-styles/index.js index ca3355596dc5f..8af3b493e4496 100644 --- a/packages/block-editor/src/components/editor-styles/index.js +++ b/packages/block-editor/src/components/editor-styles/index.js @@ -9,7 +9,7 @@ import a11yPlugin from 'colord/plugins/a11y'; * WordPress dependencies */ import { SVG } from '@wordpress/components'; -import { useMemo, memo, useCallback } from '@wordpress/element'; +import { useCallback, useMemo, memo } from '@wordpress/element'; import { useSelect } from '@wordpress/data'; /** From 8ced871bc797e8b4d3819892e01ea7133002938f Mon Sep 17 00:00:00 2001 From: Matias Benedetto Date: Fri, 20 Sep 2024 10:23:57 -0300 Subject: [PATCH 12/12] try adding currentTheme to the editor settings --- .../src/components/use-editor-fonts-resolver/index.js | 5 ++--- .../src/components/global-styles-renderer/index.js | 11 +++++++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/block-editor/src/components/use-editor-fonts-resolver/index.js b/packages/block-editor/src/components/use-editor-fonts-resolver/index.js index d5cd6ab153590..a63111614b23e 100644 --- a/packages/block-editor/src/components/use-editor-fonts-resolver/index.js +++ b/packages/block-editor/src/components/use-editor-fonts-resolver/index.js @@ -16,9 +16,8 @@ function useEditorFontsResolver() { const { currentTheme = {}, fontFamilies = [] } = useSelect( ( select ) => { return { currentTheme: - // Disable Reason: Using 'core' as string to avoid circular dependency importing from @wordpress/core-data. - // eslint-disable-next-line @wordpress/data-no-store-string-literals - select( 'core' )?.getCurrentTheme(), + select( editorStore ).getSettings()?.__experimentalFeatures + ?.currentTheme, fontFamilies: select( editorStore ).getSettings()?.__experimentalFeatures ?.typography?.fontFamilies, diff --git a/packages/edit-site/src/components/global-styles-renderer/index.js b/packages/edit-site/src/components/global-styles-renderer/index.js index 2e840a7acdc37..0b65627a7bb44 100644 --- a/packages/edit-site/src/components/global-styles-renderer/index.js +++ b/packages/edit-site/src/components/global-styles-renderer/index.js @@ -4,6 +4,7 @@ import { useEffect } from '@wordpress/element'; import { useSelect, useDispatch } from '@wordpress/data'; import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; +import { store as coreDataStore } from '@wordpress/core-data'; /** * Internal dependencies @@ -15,12 +16,18 @@ import { TEMPLATE_POST_TYPE } from '../../utils/constants'; const { useGlobalStylesOutput } = unlock( blockEditorPrivateApis ); function useGlobalStylesRenderer() { - const postType = useSelect( ( select ) => { - return select( editSiteStore ).getEditedPostType(); + const { postType, currentTheme } = useSelect( ( select ) => { + return { + postType: select( editSiteStore ).getEditedPostType(), + currentTheme: select( coreDataStore ).getCurrentTheme(), + }; } ); const [ styles, settings ] = useGlobalStylesOutput( postType !== TEMPLATE_POST_TYPE ); + + settings.currentTheme = currentTheme; + const { getSettings } = useSelect( editSiteStore ); const { updateSettings } = useDispatch( editSiteStore );