Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RichText: fix tree output after applying format #14555

Merged
merged 3 commits into from
Apr 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 34 additions & 15 deletions packages/rich-text/src/apply-format.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ import { find } from 'lodash';

import { normaliseFormats } from './normalise-formats';

function replace( array, index, value ) {
array = array.slice();
array[ index ] = value;
return array;
}

/**
* 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
Expand Down Expand Up @@ -38,15 +44,19 @@ export function applyFormat(
// If the caret is at a format of the same type, expand start and end to
// the edges of the format. This is useful to apply new attributes.
if ( startFormat ) {
while ( find( newFormats[ startIndex ], startFormat ) ) {
applyFormats( newFormats, startIndex, format );
const index = newFormats[ startIndex ].indexOf( startFormat );

while ( newFormats[ startIndex ] && newFormats[ startIndex ][ index ] === startFormat ) {
newFormats[ startIndex ] =
replace( newFormats[ startIndex ], index, format );
startIndex--;
}

endIndex++;

while ( find( newFormats[ endIndex ], startFormat ) ) {
applyFormats( newFormats, endIndex, format );
while ( newFormats[ endIndex ] && newFormats[ endIndex ][ index ] === startFormat ) {
newFormats[ endIndex ] =
replace( newFormats[ endIndex ], index, format );
endIndex++;
}
// Otherwise, insert a placeholder with the format so new input appears
Expand All @@ -58,20 +68,29 @@ export function applyFormat(
};
}
} else {
// Determine the highest position the new format can be inserted at.
let position = +Infinity;

for ( let index = startIndex; index < endIndex; index++ ) {
if ( newFormats[ index ] ) {
newFormats[ index ] = newFormats[ index ]
.filter( ( { type } ) => type !== format.type );

const length = newFormats[ index ].length;

if ( length < position ) {
position = length;
}
} else {
newFormats[ index ] = [];
position = 0;
}
}

for ( let index = startIndex; index < endIndex; index++ ) {
applyFormats( newFormats, index, format );
newFormats[ index ].splice( position, 0, format );
}
}

return normaliseFormats( { ...value, formats: newFormats } );
}

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 ];
}
}
116 changes: 114 additions & 2 deletions packages/rich-text/src/test/apply-format.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,124 @@ describe( 'applyFormat', () => {
const a2 = { type: 'a', attributes: { href: '#test' } };

it( 'should apply format', () => {
const record = {
formats: [ , , , , ],
text: 'test',
};
const expected = {
...record,
formats: [ [ em ], [ em ], [ em ], [ em ] ],
};
const result = applyFormat( deepFreeze( record ), em, 0, 4 );

expect( result ).toEqual( expected );
expect( result ).not.toBe( record );
expect( getSparseArrayLength( result.formats ) ).toBe( 4 );
} );

it( 'should apply format on top of existing format', () => {
const record = {
formats: [ [ strong ], [ strong ], [ strong ], [ strong ] ],
text: 'test',
};
const expected = {
...record,
formats: [ [ strong, em ], [ strong, em ], [ strong, em ], [ strong, em ] ],
};
const result = applyFormat( deepFreeze( record ), em, 0, 4 );

expect( result ).toEqual( expected );
expect( result ).not.toBe( record );
expect( getSparseArrayLength( result.formats ) ).toBe( 4 );
} );

it( 'should apply format and remove same format type', () => {
const record = {
formats: [ [ strong ], [ em, strong ], [ em, strong ], [ strong ] ],
text: 'test',
};
const expected = {
...record,
formats: [ [ strong, em ], [ strong, em ], [ strong, em ], [ strong, em ] ],
};
const result = applyFormat( deepFreeze( record ), em, 0, 4 );

expect( result ).toEqual( expected );
expect( result ).not.toBe( record );
expect( getSparseArrayLength( result.formats ) ).toBe( 4 );
} );

it( 'should apply format around existing format', () => {
const record = {
formats: [ , [ em ], [ em ], , ],
text: 'test',
};
const expected = {
...record,
formats: [ [ strong ], [ strong, em ], [ strong, em ], [ strong ] ],
};
const result = applyFormat( deepFreeze( record ), strong, 0, 4 );

expect( result ).toEqual( expected );
expect( result ).not.toBe( record );
expect( getSparseArrayLength( result.formats ) ).toBe( 4 );
} );

it( 'should apply format around existing format with edge right', () => {
const record = {
formats: [ , [ em ], [ em ], , ],
text: 'test',
};
const expected = {
...record,
formats: [ [ strong ], [ strong, em ], [ strong, em ], , ],
};
const result = applyFormat( deepFreeze( record ), strong, 0, 3 );

expect( result ).toEqual( expected );
expect( result ).not.toBe( record );
expect( getSparseArrayLength( result.formats ) ).toBe( 3 );
} );

it( 'should apply format around existing format with edge left', () => {
const record = {
formats: [ , [ em ], [ em ], , ],
text: 'test',
};
const expected = {
...record,
formats: [ , [ strong, em ], [ strong, em ], [ strong ] ],
};
const result = applyFormat( deepFreeze( record ), strong, 1, 4 );

expect( result ).toEqual( expected );
expect( result ).not.toBe( record );
expect( getSparseArrayLength( result.formats ) ).toBe( 3 );
} );

it( 'should apply format around existing format with break', () => {
const record = {
formats: [ , [ em ], , [ em ] ],
text: 'test',
};
const expected = {
...record,
formats: [ , [ strong, em ], [ strong ], [ strong, em ] ],
};
const result = applyFormat( deepFreeze( record ), strong, 1, 4 );

expect( result ).toEqual( expected );
expect( result ).not.toBe( record );
expect( getSparseArrayLength( result.formats ) ).toBe( 3 );
} );

it( 'should apply format crossing existing format', () => {
const record = {
formats: [ , , , , [ em ], [ em ], [ em ], , , , , , , ],
text: 'one two three',
};
const expected = {
formats: [ , , , [ strong ], [ em, strong ], [ em, strong ], [ em ], , , , , , , ],
formats: [ , , , [ strong ], [ strong, em ], [ strong, em ], [ em ], , , , , , , ],
text: 'one two three',
};
const result = applyFormat( deepFreeze( record ), strong, 3, 6 );
Expand All @@ -40,7 +152,7 @@ describe( 'applyFormat', () => {
end: 6,
};
const expected = {
formats: [ , , , [ strong ], [ em, strong ], [ em, strong ], [ em ], , , , , , , ],
formats: [ , , , [ strong ], [ strong, em ], [ strong, em ], [ em ], , , , , , , ],
text: 'one two three',
start: 3,
end: 6,
Expand Down
2 changes: 1 addition & 1 deletion packages/rich-text/src/test/toggle-format.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ describe( 'toggleFormat', () => {
end: 6,
};
const expected = {
formats: [ , , , [ strong ], [ em, strong ], [ em, strong ], [ em ], , , , , , , ],
formats: [ , , , [ strong ], [ strong, em ], [ strong, em ], [ em ], , , , , , , ],
text: 'one two three',
start: 3,
end: 6,
Expand Down