-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Handle multiple formats with format placeholder
- Loading branch information
Showing
5 changed files
with
183 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
|
||
import { find, without } from 'lodash'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
|
||
import { normaliseFormats } from './normalise-formats'; | ||
|
||
/** | ||
* Apply a format object to a Rich Text value from the given `startIndex` to the | ||
* given `endIndex`. Indices are retrieved from the selection if none are | ||
* provided. | ||
* | ||
* @param {Object} value Value to modify. | ||
* @param {Object} format Format to apply. | ||
* @param {number} startIndex Start index. | ||
* @param {number} endIndex End index. | ||
* | ||
* @return {Object} A new value with the format applied. | ||
*/ | ||
export function applyFormat( | ||
{ formats, formatPlaceholder, text, start, end }, | ||
format, | ||
startIndex = start, | ||
endIndex = end | ||
) { | ||
const newFormats = formats.slice( 0 ); | ||
const previousFormats = newFormats[ startIndex - 1 ] || []; | ||
const placeholderFormats = formatPlaceholder && formatPlaceholder.index === start && formatPlaceholder.formats; | ||
// Follow the same logic as in getActiveFormat: placeholderFormats has priority over previousFormats | ||
const activeFormats = ( placeholderFormats ? placeholderFormats : previousFormats ) || []; | ||
const hasType = find( activeFormats, { type: format.type } ); | ||
|
||
// The selection is collpased, insert a placeholder with the format so new input appears | ||
// with the format applied. | ||
if ( startIndex === endIndex ) { | ||
return { | ||
formats, | ||
text, | ||
start, | ||
end, | ||
formatPlaceholder: { | ||
index: start, | ||
formats: [ | ||
...without( activeFormats, hasType ), | ||
...! hasType && [ format ], | ||
], | ||
}, | ||
}; | ||
} | ||
|
||
for ( let index = startIndex; index < endIndex; index++ ) { | ||
applyFormats( newFormats, index, format ); | ||
} | ||
|
||
return normaliseFormats( { formats: newFormats, text, start, end } ); | ||
} | ||
|
||
function applyFormats( formats, index, format ) { | ||
if ( formats[ index ] ) { | ||
const newFormatsAtIndex = formats[ index ].filter( ( { type } ) => type !== format.type ); | ||
newFormatsAtIndex.push( format ); | ||
formats[ index ] = newFormatsAtIndex; | ||
} else { | ||
formats[ index ] = [ format ]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
/** | ||
* Internal dependencies | ||
*/ | ||
|
||
import { isFormatEqual } from './is-format-equal'; | ||
|
||
/** | ||
* Normalises formats: ensures subsequent equal formats have the same reference. | ||
* | ||
* @param {Object} value Value to normalise formats of. | ||
* | ||
* @return {Object} New value with normalised formats. | ||
*/ | ||
export function normaliseFormats( { formats, formatPlaceholder, text, start, end } ) { | ||
const newFormats = formats.slice( 0 ); | ||
|
||
newFormats.forEach( ( formatsAtIndex, index ) => { | ||
const lastFormatsAtIndex = newFormats[ index - 1 ]; | ||
|
||
if ( lastFormatsAtIndex ) { | ||
const newFormatsAtIndex = formatsAtIndex.slice( 0 ); | ||
|
||
newFormatsAtIndex.forEach( ( format, formatIndex ) => { | ||
const lastFormat = lastFormatsAtIndex[ formatIndex ]; | ||
|
||
if ( isFormatEqual( format, lastFormat ) ) { | ||
newFormatsAtIndex[ formatIndex ] = lastFormat; | ||
} | ||
} ); | ||
|
||
newFormats[ index ] = newFormatsAtIndex; | ||
} | ||
} ); | ||
|
||
return { formats: newFormats, formatPlaceholder, text, start, end }; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
|
||
import { find, without } from 'lodash'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
|
||
import { normaliseFormats } from './normalise-formats'; | ||
|
||
/** | ||
* Remove any format object from a Rich Text value by type from the given | ||
* `startIndex` to the given `endIndex`. Indices are retrieved from the | ||
* selection if none are provided. | ||
* | ||
* @param {Object} value Value to modify. | ||
* @param {string} formatType Format type to remove. | ||
* @param {number} startIndex Start index. | ||
* @param {number} endIndex End index. | ||
* | ||
* @return {Object} A new value with the format applied. | ||
*/ | ||
export function removeFormat( | ||
{ formats, formatPlaceholder, text, start, end }, | ||
formatType, | ||
startIndex = start, | ||
endIndex = end | ||
) { | ||
const newFormats = formats.slice( 0 ); | ||
let newFormatPlaceholder = null; | ||
|
||
if ( start === end && formatPlaceholder && formatPlaceholder.index === start ) { | ||
newFormatPlaceholder = { | ||
...formatPlaceholder, | ||
formats: without( formatPlaceholder.formats || [], find( formatPlaceholder.formats || [], { type: formatType } ) ), | ||
}; | ||
} | ||
|
||
// Do not remove format if selection is empty | ||
for ( let i = startIndex; i < endIndex; i++ ) { | ||
if ( newFormats[ i ] ) { | ||
filterFormats( newFormats, i, formatType ); | ||
} | ||
} | ||
|
||
return normaliseFormats( { formats: newFormats, formatPlaceholder: newFormatPlaceholder, text, start, end } ); | ||
} | ||
|
||
function filterFormats( formats, index, formatType ) { | ||
const newFormats = formats[ index ].filter( ( { type } ) => type !== formatType ); | ||
|
||
if ( newFormats.length ) { | ||
formats[ index ] = newFormats; | ||
} else { | ||
delete formats[ index ]; | ||
} | ||
} |