diff --git a/src/components/visual-editor/index.jsx b/src/components/visual-editor/index.jsx index b4b7f9f5..b464dc92 100644 --- a/src/components/visual-editor/index.jsx +++ b/src/components/visual-editor/index.jsx @@ -26,6 +26,11 @@ import { useEditorStyles } from './use-editor-styles'; import { unlock } from '../../lock-unlock'; import DefaultBlockAppender from '../default-block-appender'; import { useEditorVisible } from './use-editor-visible'; +// The Vite query parameter breaks the linter's import resolution +// eslint-disable-next-line import/no-unresolved +import defaultThemeStyles from './default-theme-styles.scss?inline'; +// eslint-disable-next-line import/no-unresolved +import commonStyles from './wp-common-styles.scss?inline'; const { ExperimentalBlockCanvas: BlockCanvas, @@ -54,7 +59,7 @@ function VisualEditor( { hideTitle } ) { const { renderingMode, - hasThemeStyleSupport, + hasThemeStyles, themeHasDisabledLayoutStyles, themeSupportsLayout, hasRootPaddingAwareAlignments, @@ -63,11 +68,14 @@ function VisualEditor( { hideTitle } ) { const _renderingMode = getRenderingMode(); const { getSettings } = unlock( select( blockEditorStore ) ); const _settings = getSettings(); + // Implies editor settings provided theme styles via the REST API. + const settingsHasStyles = _settings.styles?.length > 0; return { renderingMode: _renderingMode, - hasThemeStyleSupport: - select( editPostStore ).isFeatureActive( 'themeStyles' ), + hasThemeStyles: + select( editPostStore ).isFeatureActive( 'themeStyles' ) && + settingsHasStyles, themeSupportsLayout: _settings.supportsLayout, themeHasDisabledLayoutStyles: _settings.disableLayoutStyles, hasRootPaddingAwareAlignments: @@ -75,11 +83,17 @@ function VisualEditor( { hideTitle } ) { }; }, [] ); - const styles = useEditorStyles(); + const styles = useEditorStyles( + // `commonStyles` represent manually added notable styles that are missing. + // The styles likely absent due to them being injected by the WP Admin + // context. + commonStyles, + // Add sensible default styles if theme styles are not present. + hasThemeStyles ? '' : defaultThemeStyles + ); const editorClasses = clsx( 'gutenberg-kit-visual-editor', { - 'has-root-padding': - ! hasThemeStyleSupport || ! hasRootPaddingAwareAlignments, + 'has-root-padding': ! hasThemeStyles || ! hasRootPaddingAwareAlignments, } ); const titleClasses = clsx( @@ -87,7 +101,7 @@ function VisualEditor( { hideTitle } ) { 'editor-visual-editor__post-title-wrapper', { 'has-global-padding': - hasThemeStyleSupport && hasRootPaddingAwareAlignments, + hasThemeStyles && hasRootPaddingAwareAlignments, } ); @@ -114,7 +128,7 @@ function VisualEditor( { hideTitle } ) { { 'is-layout-flow': ! themeSupportsLayout, 'has-global-padding': - hasThemeStyleSupport && hasRootPaddingAwareAlignments, + hasThemeStyles && hasRootPaddingAwareAlignments, } ); diff --git a/src/components/visual-editor/use-editor-styles.js b/src/components/visual-editor/use-editor-styles.js index 738770fd..3c744b04 100644 --- a/src/components/visual-editor/use-editor-styles.js +++ b/src/components/visual-editor/use-editor-styles.js @@ -11,22 +11,20 @@ import { useMemo } from '@wordpress/element'; * Internal dependencies */ import { unlock } from '../../lock-unlock'; -// The Vite query parameter breaks the linter's import resolution -// eslint-disable-next-line import/no-unresolved -import defaultThemeStyles from './default-theme-styles.scss?inline'; -// eslint-disable-next-line import/no-unresolved -import commonStyles from './wp-common-styles.scss?inline'; const { getLayoutStyles } = unlock( blockEditorPrivateApis ); /** * Custom hook to retrieve and memoize editor styles. * + * @param {...any} additionalStyles Additional styles to add to the default styles. + * * @todo This should be exported from Core so no reimplementation is needed. + * @see https://github.com/WordPress/gutenberg/blob/a4d79e85a06e06b9123778e6991ac27b0bbe351d/packages/edit-post/src/components/layout/index.js#L86 * * @return {any[]} An array of editor styles. */ -export function useEditorStyles() { +export function useEditorStyles( ...additionalStyles ) { const { hasThemeStyleSupport, editorSettings } = useSelect( ( select ) => { return { hasThemeStyleSupport: @@ -35,12 +33,29 @@ export function useEditorStyles() { }; }, [] ); + const addedStyles = additionalStyles.join( '\n' ); + + // Compute the default styles. return useMemo( () => { + const presetStyles = + editorSettings.styles?.filter( + ( style ) => + style.__unstableType && style.__unstableType !== 'theme' + ) ?? []; + const defaultEditorStyles = [ ...( editorSettings?.defaultEditorStyles ?? [] ), + ...presetStyles, ]; - if ( ! editorSettings.disableLayoutStyles && ! hasThemeStyleSupport ) { + // Has theme styles if the theme supports them and if some styles were not preset styles (in which case they're theme styles). + const hasThemeStyles = + hasThemeStyleSupport && + presetStyles.length !== ( editorSettings.styles?.length ?? 0 ); + + // If theme styles are not present or displayed, ensure that + // base layout styles are still present in the editor. + if ( ! editorSettings.disableLayoutStyles && ! hasThemeStyles ) { defaultEditorStyles.push( { css: getLayoutStyles( { style: {}, @@ -52,24 +67,20 @@ export function useEditorStyles() { } ); } - if ( ! hasThemeStyleSupport ) { - defaultEditorStyles.push( { - css: defaultThemeStyles, - } ); - } - - const baseStyles = hasThemeStyleSupport + const baseStyles = hasThemeStyles ? editorSettings.styles ?? [] : defaultEditorStyles; - // `commonStyles` represent manually added notable styles that are missing. - // The styles likely absent due to them being injected by the WP Admin - // context. - return [ { css: commonStyles }, ...baseStyles ]; + if ( addedStyles ) { + return [ ...baseStyles, { css: addedStyles } ]; + } + + return baseStyles; }, [ editorSettings.defaultEditorStyles, editorSettings.disableLayoutStyles, editorSettings.styles, hasThemeStyleSupport, + addedStyles, ] ); } diff --git a/src/components/visual-editor/wp-common-styles.scss b/src/components/visual-editor/wp-common-styles.scss index 6b8f6060..c867161d 100644 --- a/src/components/visual-editor/wp-common-styles.scss +++ b/src/components/visual-editor/wp-common-styles.scss @@ -1,6 +1,8 @@ // Manually added styles that are missing from the editor settings, likely // because the styles are injected by the WP Admin context, which is not present // in the REST API endpoint. +// +// @todo: Research a different approach that keeps these styles up to date. // Constraints for the post title. .editor-visual-editor__post-title-wrapper @@ -44,3 +46,333 @@ margin-block-start: 1.2rem; margin-block-end: 0; } + +// Default colors and presets. +:root { + --wp--preset--aspect-ratio--square: 1; + --wp--preset--aspect-ratio--4-3: 4/3; + --wp--preset--aspect-ratio--3-4: 3/4; + --wp--preset--aspect-ratio--3-2: 3/2; + --wp--preset--aspect-ratio--2-3: 2/3; + --wp--preset--aspect-ratio--16-9: 16/9; + --wp--preset--aspect-ratio--9-16: 9/16; + --wp--preset--color--black: #000000; + --wp--preset--color--cyan-bluish-gray: #abb8c3; + --wp--preset--color--white: #ffffff; + --wp--preset--color--pale-pink: #f78da7; + --wp--preset--color--vivid-red: #cf2e2e; + --wp--preset--color--luminous-vivid-orange: #ff6900; + --wp--preset--color--luminous-vivid-amber: #fcb900; + --wp--preset--color--light-green-cyan: #7bdcb5; + --wp--preset--color--vivid-green-cyan: #00d084; + --wp--preset--color--pale-cyan-blue: #8ed1fc; + --wp--preset--color--vivid-cyan-blue: #0693e3; + --wp--preset--color--vivid-purple: #9b51e0; + --wp--preset--color--dark-gray: #28303d; + --wp--preset--color--gray: #39414d; + --wp--preset--color--green: #d1e4dd; + --wp--preset--color--blue: #d1dfe4; + --wp--preset--color--purple: #d1d1e4; + --wp--preset--color--red: #e4d1d1; + --wp--preset--color--orange: #e4dad1; + --wp--preset--color--yellow: #eeeadd; + --wp--preset--gradient--vivid-cyan-blue-to-vivid-purple: linear-gradient( + 135deg, + rgba(6, 147, 227, 1) 0%, + rgb(155, 81, 224) 100% + ); + --wp--preset--gradient--light-green-cyan-to-vivid-green-cyan: linear-gradient( + 135deg, + rgb(122, 220, 180) 0%, + rgb(0, 208, 130) 100% + ); + --wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange: linear-gradient( + 135deg, + rgba(252, 185, 0, 1) 0%, + rgba(255, 105, 0, 1) 100% + ); + --wp--preset--gradient--luminous-vivid-orange-to-vivid-red: linear-gradient( + 135deg, + rgba(255, 105, 0, 1) 0%, + rgb(207, 46, 46) 100% + ); + --wp--preset--gradient--very-light-gray-to-cyan-bluish-gray: linear-gradient( + 135deg, + rgb(238, 238, 238) 0%, + rgb(169, 184, 195) 100% + ); + --wp--preset--gradient--cool-to-warm-spectrum: linear-gradient( + 135deg, + rgb(74, 234, 220) 0%, + rgb(151, 120, 209) 20%, + rgb(207, 42, 186) 40%, + rgb(238, 44, 130) 60%, + rgb(251, 105, 98) 80%, + rgb(254, 248, 76) 100% + ); + --wp--preset--gradient--blush-light-purple: linear-gradient( + 135deg, + rgb(255, 206, 236) 0%, + rgb(152, 150, 240) 100% + ); + --wp--preset--gradient--blush-bordeaux: linear-gradient( + 135deg, + rgb(254, 205, 165) 0%, + rgb(254, 45, 45) 50%, + rgb(107, 0, 62) 100% + ); + --wp--preset--gradient--luminous-dusk: linear-gradient( + 135deg, + rgb(255, 203, 112) 0%, + rgb(199, 81, 192) 50%, + rgb(65, 88, 208) 100% + ); + --wp--preset--gradient--pale-ocean: linear-gradient( + 135deg, + rgb(255, 245, 203) 0%, + rgb(182, 227, 212) 50%, + rgb(51, 167, 181) 100% + ); + --wp--preset--gradient--electric-grass: linear-gradient( + 135deg, + rgb(202, 248, 128) 0%, + rgb(113, 206, 126) 100% + ); + --wp--preset--gradient--midnight: linear-gradient( + 135deg, + rgb(2, 3, 129) 0%, + rgb(40, 116, 252) 100% + ); + --wp--preset--gradient--purple-to-yellow: linear-gradient( + 160deg, + #d1d1e4 0%, + #eeeadd 100% + ); + --wp--preset--gradient--yellow-to-purple: linear-gradient( + 160deg, + #eeeadd 0%, + #d1d1e4 100% + ); + --wp--preset--gradient--green-to-yellow: linear-gradient( + 160deg, + #d1e4dd 0%, + #eeeadd 100% + ); + --wp--preset--gradient--yellow-to-green: linear-gradient( + 160deg, + #eeeadd 0%, + #d1e4dd 100% + ); + --wp--preset--gradient--red-to-yellow: linear-gradient( + 160deg, + #e4d1d1 0%, + #eeeadd 100% + ); + --wp--preset--gradient--yellow-to-red: linear-gradient( + 160deg, + #eeeadd 0%, + #e4d1d1 100% + ); + --wp--preset--gradient--purple-to-red: linear-gradient( + 160deg, + #d1d1e4 0%, + #e4d1d1 100% + ); + --wp--preset--gradient--red-to-purple: linear-gradient( + 160deg, + #e4d1d1 0%, + #d1d1e4 100% + ); + --wp--preset--font-size--small: 18px; + --wp--preset--font-size--medium: 20px; + --wp--preset--font-size--large: 24px; + --wp--preset--font-size--x-large: 42px; + --wp--preset--font-size--extra-small: 16px; + --wp--preset--font-size--normal: 20px; + --wp--preset--font-size--extra-large: 40px; + --wp--preset--font-size--huge: 96px; + --wp--preset--font-size--gigantic: 144px; + --wp--preset--spacing--20: 0.44rem; + --wp--preset--spacing--30: 0.67rem; + --wp--preset--spacing--40: 1rem; + --wp--preset--spacing--50: 1.5rem; + --wp--preset--spacing--60: 2.25rem; + --wp--preset--spacing--70: 3.38rem; + --wp--preset--spacing--80: 5.06rem; + --wp--preset--shadow--natural: 6px 6px 9px rgba(0, 0, 0, 0.2); + --wp--preset--shadow--deep: 12px 12px 50px rgba(0, 0, 0, 0.4); + --wp--preset--shadow--sharp: 6px 6px 0px rgba(0, 0, 0, 0.2); + --wp--preset--shadow--outlined: 6px 6px 0px -3px rgba(255, 255, 255, 1), + 6px 6px rgba(0, 0, 0, 1); + --wp--preset--shadow--crisp: 6px 6px 0px rgba(0, 0, 0, 1); +} + +// Color utilities. +.has-black-color { + color: var(--wp--preset--color--black) !important; +} +.has-cyan-bluish-gray-color { + color: var(--wp--preset--color--cyan-bluish-gray) !important; +} +.has-white-color { + color: var(--wp--preset--color--white) !important; +} +.has-pale-pink-color { + color: var(--wp--preset--color--pale-pink) !important; +} +.has-vivid-red-color { + color: var(--wp--preset--color--vivid-red) !important; +} +.has-luminous-vivid-orange-color { + color: var(--wp--preset--color--luminous-vivid-orange) !important; +} +.has-luminous-vivid-amber-color { + color: var(--wp--preset--color--luminous-vivid-amber) !important; +} +.has-light-green-cyan-color { + color: var(--wp--preset--color--light-green-cyan) !important; +} +.has-vivid-green-cyan-color { + color: var(--wp--preset--color--vivid-green-cyan) !important; +} +.has-pale-cyan-blue-color { + color: var(--wp--preset--color--pale-cyan-blue) !important; +} +.has-vivid-cyan-blue-color { + color: var(--wp--preset--color--vivid-cyan-blue) !important; +} +.has-vivid-purple-color { + color: var(--wp--preset--color--vivid-purple) !important; +} +.has-black-background-color { + background-color: var(--wp--preset--color--black) !important; +} +.has-cyan-bluish-gray-background-color { + background-color: var(--wp--preset--color--cyan-bluish-gray) !important; +} +.has-white-background-color { + background-color: var(--wp--preset--color--white) !important; +} +.has-pale-pink-background-color { + background-color: var(--wp--preset--color--pale-pink) !important; +} +.has-vivid-red-background-color { + background-color: var(--wp--preset--color--vivid-red) !important; +} +.has-luminous-vivid-orange-background-color { + background-color: var( + --wp--preset--color--luminous-vivid-orange + ) !important; +} +.has-luminous-vivid-amber-background-color { + background-color: var(--wp--preset--color--luminous-vivid-amber) !important; +} +.has-light-green-cyan-background-color { + background-color: var(--wp--preset--color--light-green-cyan) !important; +} +.has-vivid-green-cyan-background-color { + background-color: var(--wp--preset--color--vivid-green-cyan) !important; +} +.has-pale-cyan-blue-background-color { + background-color: var(--wp--preset--color--pale-cyan-blue) !important; +} +.has-vivid-cyan-blue-background-color { + background-color: var(--wp--preset--color--vivid-cyan-blue) !important; +} +.has-vivid-purple-background-color { + background-color: var(--wp--preset--color--vivid-purple) !important; +} +.has-black-border-color { + border-color: var(--wp--preset--color--black) !important; +} +.has-cyan-bluish-gray-border-color { + border-color: var(--wp--preset--color--cyan-bluish-gray) !important; +} +.has-white-border-color { + border-color: var(--wp--preset--color--white) !important; +} +.has-pale-pink-border-color { + border-color: var(--wp--preset--color--pale-pink) !important; +} +.has-vivid-red-border-color { + border-color: var(--wp--preset--color--vivid-red) !important; +} +.has-luminous-vivid-orange-border-color { + border-color: var(--wp--preset--color--luminous-vivid-orange) !important; +} +.has-luminous-vivid-amber-border-color { + border-color: var(--wp--preset--color--luminous-vivid-amber) !important; +} +.has-light-green-cyan-border-color { + border-color: var(--wp--preset--color--light-green-cyan) !important; +} +.has-vivid-green-cyan-border-color { + border-color: var(--wp--preset--color--vivid-green-cyan) !important; +} +.has-pale-cyan-blue-border-color { + border-color: var(--wp--preset--color--pale-cyan-blue) !important; +} +.has-vivid-cyan-blue-border-color { + border-color: var(--wp--preset--color--vivid-cyan-blue) !important; +} +.has-vivid-purple-border-color { + border-color: var(--wp--preset--color--vivid-purple) !important; +} +.has-vivid-cyan-blue-to-vivid-purple-gradient-background { + background: var( + --wp--preset--gradient--vivid-cyan-blue-to-vivid-purple + ) !important; +} +.has-light-green-cyan-to-vivid-green-cyan-gradient-background { + background: var( + --wp--preset--gradient--light-green-cyan-to-vivid-green-cyan + ) !important; +} +.has-luminous-vivid-amber-to-luminous-vivid-orange-gradient-background { + background: var( + --wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange + ) !important; +} +.has-luminous-vivid-orange-to-vivid-red-gradient-background { + background: var( + --wp--preset--gradient--luminous-vivid-orange-to-vivid-red + ) !important; +} +.has-very-light-gray-to-cyan-bluish-gray-gradient-background { + background: var( + --wp--preset--gradient--very-light-gray-to-cyan-bluish-gray + ) !important; +} +.has-cool-to-warm-spectrum-gradient-background { + background: var(--wp--preset--gradient--cool-to-warm-spectrum) !important; +} +.has-blush-light-purple-gradient-background { + background: var(--wp--preset--gradient--blush-light-purple) !important; +} +.has-blush-bordeaux-gradient-background { + background: var(--wp--preset--gradient--blush-bordeaux) !important; +} +.has-luminous-dusk-gradient-background { + background: var(--wp--preset--gradient--luminous-dusk) !important; +} +.has-pale-ocean-gradient-background { + background: var(--wp--preset--gradient--pale-ocean) !important; +} +.has-electric-grass-gradient-background { + background: var(--wp--preset--gradient--electric-grass) !important; +} +.has-midnight-gradient-background { + background: var(--wp--preset--gradient--midnight) !important; +} +.has-small-font-size { + font-size: var(--wp--preset--font-size--small) !important; +} +.has-medium-font-size { + font-size: var(--wp--preset--font-size--medium) !important; +} +.has-large-font-size { + font-size: var(--wp--preset--font-size--large) !important; +} +.has-x-large-font-size { + font-size: var(--wp--preset--font-size--x-large) !important; +} diff --git a/src/utils/editor-settings.js b/src/utils/editor-settings.js new file mode 100644 index 00000000..b83a322b --- /dev/null +++ b/src/utils/editor-settings.js @@ -0,0 +1,58 @@ +/** + * WordPress dependencies + */ +import defaultEditorStyles from '@wordpress/block-editor/build-style/default-editor-styles.css?inline'; +import { store as editorStore } from '@wordpress/editor'; +import { select } from '@wordpress/data'; + +/** + * Returns a default editor settings object to use when a site cannot provide + * its own editor settings due to lacking the necessary experimental REST API + * endpoint provided by the Gutenberg plugin. + * + * @see https://github.com/WordPress/gutenberg/blob/d97807b50f756126798a04a5ae94745aee19356d/lib/experimental/class-wp-rest-block-editor-settings-controller.php + * + * @return {Object} The default editor settings object + */ +export function getDefaultEditorSettings() { + const settings = select( editorStore ).getEditorSettings(); + const defaultEditorColors = settings?.colors; + const defaultEditorGradients = settings?.gradients; + + return { + /** + * These styles are used if the "no theme styles" options is triggered or on + * themes without their own editor styles. + * + * @see https://github.com/WordPress/wordpress-develop/blob/db9654e6d386c2c69401b9c77c6ce1ae7460a6e4/src/wp-includes/block-editor.php#L218C4-L218C23 + */ + defaultEditorStyles: [ { css: defaultEditorStyles } ], + __experimentalFeatures: { + /** + * Avoid errors from blocks relying upon block settings. + * + * @see https://github.com/WordPress/gutenberg/pull/64448 + */ + blocks: {}, + /** + * Ensure themes lacking their own theme styles can customize blocks using + * the default editor colors and gradients. Mirrors the approach taken by + * the Gutenberg Mobile editor. + * + * @see https://github.com/WordPress/gutenberg/blob/d97807b50f756126798a04a5ae94745aee19356d/packages/block-editor/src/components/global-styles/use-global-styles-context.native.js#L410-L433 + */ + color: { + text: true, + background: true, + palette: { + default: defaultEditorColors, + }, + gradients: { + default: defaultEditorGradients, + }, + defaultPalette: defaultEditorColors?.length > 0, + defaultGradients: defaultEditorGradients?.length > 0, + }, + }, + }; +} diff --git a/src/utils/editor.jsx b/src/utils/editor.jsx index 52d28b13..195cadac 100644 --- a/src/utils/editor.jsx +++ b/src/utils/editor.jsx @@ -1,8 +1,3 @@ -/** - * WordPress dependencies - */ -import defaultEditorStyles from '@wordpress/block-editor/build-style/default-editor-styles.css?inline'; - /** * Internal dependencies */ @@ -14,6 +9,7 @@ import { store as preferencesStore } from '@wordpress/preferences'; import { registerCoreBlocks } from '@wordpress/block-library'; import { unregisterDisallowedBlocks } from './blocks'; import { getGBKit, getPost } from './bridge'; +import { getDefaultEditorSettings } from './editor-settings'; /** * Configure editor settings and styles, and render the editor. @@ -28,9 +24,7 @@ import { getGBKit, getPost } from './bridge'; export function initializeEditor( { allowedBlockTypes } = {} ) { const { themeStyles, hideTitle, editorSettings } = getGBKit(); - const settings = editorSettings || { - defaultEditorStyles: [ { css: defaultEditorStyles } ], - }; + const settings = editorSettings || getDefaultEditorSettings(); dispatch( editorStore ).updateEditorSettings( settings ); const preferenceDispatch = dispatch( preferencesStore );