diff --git a/packages/format-library/src/text-color/index.js b/packages/format-library/src/text-color/index.js index a582197503f3c..77824a26d69ba 100644 --- a/packages/format-library/src/text-color/index.js +++ b/packages/format-library/src/text-color/index.js @@ -125,5 +125,23 @@ export const textColor = { style: 'style', class: 'class', }, + /* + * Since this format relies on the tag, it's important to + * prevent the default yellow background color applied by most + * browsers. The solution is to detect when this format is used with a + * text color but no background color, and in such cases to override + * the default styling with a transparent background. + * + * @see https://github.com/WordPress/gutenberg/pull/35516 + */ + __unstableFilterAttributeValue( key, value ) { + if ( key !== 'style' ) return value; + // We should not add a background-color if it's already set + if ( value && value.includes( 'background-color' ) ) return value; + const addedCSS = [ 'background-color', 'rgba(0, 0, 0, 0)' ].join( ':' ); + // Prepend `addedCSS` to avoid a double `;;` as any the existing CSS + // rules will already include a `;`. + return value ? [ addedCSS, value ].join( ';' ) : addedCSS; + }, edit: TextColorEdit, }; diff --git a/packages/rich-text/src/component/index.js b/packages/rich-text/src/component/index.js index b5c4f081490e5..d73e89f2e398c 100644 --- a/packages/rich-text/src/component/index.js +++ b/packages/rich-text/src/component/index.js @@ -101,6 +101,21 @@ export function useRichText( { if ( ! record.current ) { setRecordFromProps(); + // Sometimes formats are added programmatically and we need to make + // sure it's persisted to the block store / markup. If these formats + // are not applied, they could cause inconsistencies between the data + // in the visual editor and the frontend. Right now, it's only relevant + // to the `core/text-color` format, which is applied at runtime in + // certain circunstances. See the `__unstableFilterAttributeValue` + // function in `packages/format-library/src/text-color/index.js`. + // @todo find a less-hacky way of solving this. + + const hasRelevantInitFormat = + record.current?.formats[ 0 ]?.[ 0 ]?.type === 'core/text-color'; + + if ( hasRelevantInitFormat ) { + handleChangesUponInit( record.current ); + } } else if ( selectionStart !== record.current.start || selectionEnd !== record.current.end @@ -153,6 +168,31 @@ export function useRichText( { forceRender(); } + function handleChangesUponInit( newRecord ) { + record.current = newRecord; + + _value.current = toHTMLString( { + value: __unstableBeforeSerialize + ? { + ...newRecord, + formats: __unstableBeforeSerialize( newRecord ), + } + : newRecord, + multilineTag, + preserveWhiteSpace, + } ); + + const { formats, text } = newRecord; + + registry.batch( () => { + onChange( _value.current, { + __unstableFormats: formats, + __unstableText: text, + } ); + } ); + forceRender(); + } + function applyFromProps() { setRecordFromProps(); applyRecord( record.current ); diff --git a/packages/rich-text/src/create.js b/packages/rich-text/src/create.js index 39f0a3708c75f..02cdda5d5af58 100644 --- a/packages/rich-text/src/create.js +++ b/packages/rich-text/src/create.js @@ -43,14 +43,6 @@ function createEmptyValue() { }; } -function simpleFindKey( object, value ) { - for ( const key in object ) { - if ( object[ key ] === value ) { - return key; - } - } -} - function toFormat( { type, attributes } ) { let formatType; @@ -94,15 +86,33 @@ function toFormat( { type, attributes } ) { const registeredAttributes = {}; const unregisteredAttributes = {}; + const _attributes = { ...attributes }; - for ( const name in attributes ) { - const key = simpleFindKey( formatType.attributes, name ); + for ( const key in formatType.attributes ) { + const name = formatType.attributes[ key ]; - if ( key ) { - registeredAttributes[ key ] = attributes[ name ]; - } else { - unregisteredAttributes[ name ] = attributes[ name ]; + registeredAttributes[ key ] = _attributes[ name ]; + + if ( formatType.__unstableFilterAttributeValue ) { + registeredAttributes[ + key + ] = formatType.__unstableFilterAttributeValue( + key, + registeredAttributes[ key ] + ); } + + // delete the attribute and what's left is considered + // to be unregistered. + delete _attributes[ name ]; + + if ( typeof registeredAttributes[ key ] === 'undefined' ) { + delete registeredAttributes[ key ]; + } + } + + for ( const name in _attributes ) { + unregisteredAttributes[ name ] = attributes[ name ]; } return {