Skip to content

Commit

Permalink
Implement selection
Browse files Browse the repository at this point in the history
  • Loading branch information
ellatrix committed Nov 14, 2023
1 parent 01bd886 commit 467e637
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 61 deletions.
23 changes: 5 additions & 18 deletions packages/rich-text/src/test/__snapshots__/to-dom.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ exports[`recordToDom should create a value with formatting 1`] = `
>
test
</em>
</body>
`;

Expand All @@ -16,9 +15,11 @@ exports[`recordToDom should create a value with formatting for split tags 1`] =
<em
data-rich-text-format-boundary="true"
>
test
te
</em>
<em>
st
</em>
</body>
`;

Expand All @@ -30,7 +31,6 @@ exports[`recordToDom should create a value with formatting with attributes 1`] =
>
test
</a>
</body>
`;

Expand All @@ -55,7 +55,6 @@ exports[`recordToDom should create a value with image object and formatting 1`]
/>
</em>
</body>
`;

Expand All @@ -82,7 +81,6 @@ exports[`recordToDom should create a value with image object and text before 1`]
/>
</em>
</body>
`;

Expand All @@ -95,7 +93,6 @@ exports[`recordToDom should create a value with nested formatting 1`] = `
test
</strong>
</em>
</body>
`;

Expand All @@ -107,14 +104,12 @@ exports[`recordToDom should create a value without formatting 1`] = `

exports[`recordToDom should create an empty value 1`] = `
<body>

</body>
`;

exports[`recordToDom should create an empty value from empty tags 1`] = `
<body>

</body>
`;
Expand Down Expand Up @@ -146,7 +141,6 @@ exports[`recordToDom should filter format boundary attributes 1`] = `
>
test
</strong>
</body>
`;

Expand All @@ -171,9 +165,8 @@ exports[`recordToDom should handle br with formatting 1`] = `
data-rich-text-line-break="true"
/>

</em>

</body>
`;

Expand Down Expand Up @@ -228,7 +221,6 @@ exports[`recordToDom should ignore manually added object replacement character w
>
hi
</em>
</body>
`;

Expand All @@ -246,7 +238,6 @@ exports[`recordToDom should not error with overlapping formats (1) 1`] = `
2
</strong>
</a>
</body>
`;

Expand All @@ -262,13 +253,11 @@ exports[`recordToDom should not error with overlapping formats (2) 1`] = `
</em>
<strong>
<a
data-rich-text-format-boundary="true"
href="#"
>
2
</a>
</strong>
</body>
`;

Expand All @@ -285,7 +274,6 @@ exports[`recordToDom should preserve emoji in formatting 1`] = `
>
🍒
</em>
</body>
`;

Expand All @@ -297,7 +285,6 @@ exports[`recordToDom should preserve non breaking space 1`] = `

exports[`recordToDom should remove padding 1`] = `
<body>

</body>
`;
6 changes: 6 additions & 0 deletions packages/rich-text/src/test/concat.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,22 @@ describe( 'concat', () => {
it( 'should merge records', () => {
const one = {
formats: [ , , [ em ] ],
_formats: new Map( [ [ em, [ 2, 3 ] ] ] ),
replacements: [ , , , ],
text: 'one',
};
const two = {
formats: [ [ em ], , , ],
_formats: new Map( [ [ { ...em }, [ 0, 1 ] ] ] ),
replacements: [ , , , ],
text: 'two',
};
const three = {
formats: [ , , [ em ], [ em ], , , ],
_formats: new Map( [
[ em, [ 2, 3 ] ],
[ { ...em }, [ 3, 4 ] ],
] ),
replacements: [ , , , , , , ],
text: 'onetwo',
};
Expand Down
11 changes: 6 additions & 5 deletions packages/rich-text/src/test/helpers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ export const spec = [
endContainer: element.querySelector( 'em' ),
} ),
startPath: [ 0, 0, 0 ],
endPath: [ 0, 0, 2 ],
endPath: [ 1, 0, 0 ],
record: {
start: 0,
end: 2,
Expand Down Expand Up @@ -477,8 +477,8 @@ export const spec = [
endOffset: 1,
endContainer: element.firstChild,
} ),
startPath: [ 0, 0, 0, 1 ],
endPath: [ 0, 0, 0, 1 ],
startPath: [ 0, 1, 0, 0 ],
endPath: [ 0, 1, 0, 0 ],
record: {
start: 1,
end: 1,
Expand All @@ -503,8 +503,8 @@ export const spec = [
endOffset: 1,
endContainer: element.firstChild,
} ),
startPath: [ 0, 0, 0, 1 ],
endPath: [ 0, 0, 0, 1 ],
startPath: [ 1, 0, 0, 0 ],
endPath: [ 1, 0, 0, 0 ],
record: {
start: 1,
end: 1,
Expand Down Expand Up @@ -779,6 +779,7 @@ export const specWithRegistration = [
html: '<a class="non-editable">a</a>',
value: {
formats: [ , ],
_formats: new Map(),
replacements: [
{
type: 'my-plugin/non-editable',
Expand Down
142 changes: 104 additions & 38 deletions packages/rich-text/src/to-tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import { getActiveFormats } from './get-active-formats';
import { getFormatType } from './get-format-type';
import { OBJECT_REPLACEMENT_CHARACTER, ZWNBSP } from './special-characters';
import { ZWNBSP } from './special-characters';

function restoreOnAttributes( attributes, isEditableTree ) {
if ( isEditableTree ) {
Expand Down Expand Up @@ -110,39 +110,20 @@ function fromFormat( {
};
}

/**
* Checks if both arrays of formats up until a certain index are equal.
*
* @param {Array} a Array of formats to compare.
* @param {Array} b Array of formats to compare.
* @param {number} index Index to check until.
*/
function isEqualUntil( a, b, index ) {
do {
if ( a[ index ] !== b[ index ] ) {
return false;
}
} while ( index-- );

return true;
}

export function toTree( {
value,
createEmpty,
append,
getLastChild,
getParent,
isText,
getText,
remove,
appendText,
onStartIndex,
onEndIndex,
onStartIndex = () => {},
onEndIndex = () => {},
isEditableTree,
placeholder,
} ) {
const { formats, _formats, replacements, text, start, end } = value;
const { _formats, replacements, text, start, end } = value;

const tags = [];

Expand Down Expand Up @@ -187,20 +168,51 @@ export function toTree( {
const activeFormats = getActiveFormats( value );
const deepestActiveFormat = activeFormats[ activeFormats.length - 1 ];

if ( text.length === 0 ) {
const _text = append( tree, ZWNBSP );
if ( start === 0 ) {
onStartIndex( tree, _text, 0 );
}
if ( end === 0 ) {
onEndIndex( tree, _text, 0 );
}
if ( placeholder ) {
append( tree, {
type: 'span',
attributes: {
'data-rich-text-placeholder': placeholder,
// Necessary to prevent the placeholder from catching
// selection and being editable.
style: 'pointer-events:none;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;',
},
} );
}
return tree;
}

let currentNode = tree;
let lastPos = 0;

for ( const tag of tags ) {
if ( lastPos !== tag.pos ) {
const _text = append(
currentNode,
text.substring( lastPos, tag.pos )
);
let _text = getLastChild( currentNode );
if ( ! _text || ! isText( _text ) ) {
_text = append( currentNode, '' );
}

if ( lastPos === start ) {
onStartIndex( tree, _text, 0 );
}
if ( lastPos === end ) {
onEndIndex( tree, _text, 0 );
}

appendText( _text, text.substring( lastPos, tag.pos ) );
// check if text contains selection start or end
if ( lastPos < start && tag.pos > start ) {
if ( lastPos < start && tag.pos >= start ) {
onStartIndex( tree, _text, start - lastPos );
}
if ( lastPos < end && tag.pos > end ) {
if ( lastPos < end && tag.pos >= end ) {
onEndIndex( tree, _text, end - lastPos );
}
}
Expand All @@ -209,7 +221,7 @@ export function toTree( {
const { type, tagName, attributes, unregisteredAttributes } =
tag.format;
const boundaryClass =
-isEditableTree && tag.format === deepestActiveFormat;
isEditableTree && tag.format === deepestActiveFormat;
currentNode = append(
currentNode,
fromFormat( {
Expand All @@ -227,20 +239,74 @@ export function toTree( {
currentNode = getParent( currentNode );
lastPos = tag.pos;
} else if ( tag.type === 'replacement' ) {
append(
currentNode,
fromFormat( {
...tag.format,
isEditableTree,
} )
);
let _text = getLastChild( currentNode );
if ( ! _text || ! isText( _text ) ) {
_text = append( currentNode, '' );
}

if ( lastPos === start ) {
onStartIndex( tree, _text, 0 );
}
if ( lastPos === end ) {
onEndIndex( tree, _text, 0 );
}
const formatType = getFormatType( tag.format.type );
if ( formatType?.contentEditable === false ) {
// For non editable formats, render the stored inner HTML.
const replacementNode = append(
currentNode,
fromFormat( {
...tag.format,
isEditableTree,
} )
);

if ( tag.format.innerHTML ) {
append( replacementNode, {
html: tag.format.innerHTML,
} );
}
} else {
append(
currentNode,
fromFormat( {
...tag.format,
isEditableTree,
object: true,
} )
);
}

_text = append( currentNode, '' );
lastPos = tag.pos + 1;

if ( lastPos === start ) {
onStartIndex( tree, _text, 0 );
}
if ( lastPos === end ) {
onEndIndex( tree, _text, 0 );
}

if ( tag.format.type === 'br' && text.length === lastPos ) {
append( currentNode, ZWNBSP );
}
}
}

if ( lastPos !== text.length ) {
const _text = append( currentNode, text.substring( lastPos ) );
let _text = getLastChild( currentNode );
if ( ! _text || ! isText( _text ) ) {
_text = append( currentNode, '' );
}

if ( lastPos === start ) {
onStartIndex( tree, _text, 0 );
}
if ( lastPos === end ) {
onEndIndex( tree, _text, 0 );
}

appendText( _text, text.substring( lastPos ) );
// check if text contains selection start or end
if ( lastPos < start ) {
onStartIndex( tree, _text, start - lastPos );
Expand Down

0 comments on commit 467e637

Please sign in to comment.