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

Footnotes: save numbering through the entity provider #52423

Merged
merged 9 commits into from
Jul 10, 2023
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
8 changes: 2 additions & 6 deletions packages/block-library/src/footnotes/format.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,9 @@ import { name } from './block.json';
export const formatName = 'core/footnote';
export const format = {
title: __( 'Footnote' ),
tagName: 'a',
tagName: 'sup',
className: 'fn',
attributes: {
id: 'id',
href: 'href',
'data-fn': 'data-fn',
},
contentEditable: false,
Expand All @@ -50,11 +48,9 @@ export const format = {
{
type: formatName,
attributes: {
href: '#' + id,
id: `${ id }-link`,
'data-fn': id,
},
innerHTML: '*',
innerHTML: `<a href="#${ id }" id="${ id }-link">*</a>`,
},
value.end,
value.end
Expand Down
6 changes: 4 additions & 2 deletions packages/block-library/src/footnotes/style.scss
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// These styles are for backwards compatibility with the old footnotes anchors.
// Can be removed in the future.
.editor-styles-wrapper,
.entry-content {
counter-reset: footnotes;
}

[data-fn].fn {
a[data-fn].fn {
vertical-align: super;
font-size: smaller;
counter-increment: footnotes;
Expand All @@ -12,7 +14,7 @@
text-indent: -9999999px;
}

[data-fn].fn::after {
a[data-fn].fn::after {
content: "[" counter(footnotes) "]";
text-indent: 0;
float: left;
Expand Down
77 changes: 72 additions & 5 deletions packages/core-data/src/entity-provider.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,9 +191,10 @@ export function useEntityBlockEditor( kind, name, { id: _id } = {} ) {

const updateFootnotes = useCallback(
( _blocks ) => {
if ( ! meta ) return;
const output = { blocks: _blocks };
if ( ! meta ) return output;
// If meta.footnotes is empty, it means the meta is not registered.
if ( meta.footnotes === undefined ) return {};
if ( meta.footnotes === undefined ) return output;

const { getRichTextValues } = unlock( blockEditorPrivateApis );
const _content = getRichTextValues( _blocks ).join( '' ) || '';
Expand All @@ -215,7 +216,8 @@ export function useEntityBlockEditor( kind, name, { id: _id } = {} ) {
: [];
const currentOrder = footnotes.map( ( fn ) => fn.id );

if ( currentOrder.join( '' ) === newOrder.join( '' ) ) return;
if ( currentOrder.join( '' ) === newOrder.join( '' ) )
return output;

const newFootnotes = newOrder.map(
( fnId ) =>
Expand All @@ -226,6 +228,71 @@ export function useEntityBlockEditor( kind, name, { id: _id } = {} ) {
}
);

function updateAttributes( attributes ) {
attributes = { ...attributes };

for ( const key in attributes ) {
const value = attributes[ key ];

if ( Array.isArray( value ) ) {
attributes[ key ] = value.map( updateAttributes );
continue;
}

if ( typeof value !== 'string' ) {
continue;
}

if ( value.indexOf( 'data-fn' ) === -1 ) {
continue;
}

// When we store rich text values, this would no longer
// require a regex.
const regex =
/(<sup[^>]+data-fn="([^"]+)"[^>]*><a[^>]*>)[\d*]*<\/a><\/sup>/g;

attributes[ key ] = value.replace(
regex,
( match, opening, fnId ) => {
const index = newOrder.indexOf( fnId );
return `${ opening }${ index + 1 }</a></sup>`;
}
);

const compatRegex =
/<a[^>]+data-fn="([^"]+)"[^>]*>\*<\/a>/g;

attributes[ key ] = attributes[ key ].replace(
compatRegex,
( match, fnId ) => {
const index = newOrder.indexOf( fnId );
return `<sup data-fn="${ fnId }" class="fn"><a href="#${ fnId }" id="${ fnId }-link">${
index + 1
}</a></sup>`;
}
);
}

return attributes;
}

function updateBlocksAttributes( __blocks ) {
return __blocks.map( ( block ) => {
return {
...block,
attributes: updateAttributes( block.attributes ),
innerBlocks: updateBlocksAttributes(
block.innerBlocks
),
};
} );
}

// We need to go through all block attributs deeply and update the
// footnote anchor numbering (textContent) to match the new order.
const newBlocks = updateBlocksAttributes( _blocks );

oldFootnotes = {
...oldFootnotes,
...footnotes.reduce( ( acc, fn ) => {
Expand All @@ -241,6 +308,7 @@ export function useEntityBlockEditor( kind, name, { id: _id } = {} ) {
...meta,
footnotes: JSON.stringify( newFootnotes ),
},
blocks: newBlocks,
};
},
[ meta ]
Expand All @@ -258,7 +326,6 @@ export function useEntityBlockEditor( kind, name, { id: _id } = {} ) {
// to make sure the edit makes the post dirty and creates
// a new undo level.
const edits = {
blocks: newBlocks,
selection,
content: ( { blocks: blocksForSerialization = [] } ) =>
__unstableSerializeAndClean( blocksForSerialization ),
Expand All @@ -282,7 +349,7 @@ export function useEntityBlockEditor( kind, name, { id: _id } = {} ) {
( newBlocks, options ) => {
const { selection } = options;
const footnotesChanges = updateFootnotes( newBlocks );
const edits = { blocks: newBlocks, selection, ...footnotesChanges };
const edits = { selection, ...footnotesChanges };

editEntityRecord( kind, name, id, edits, { isCached: true } );
},
Expand Down
7 changes: 6 additions & 1 deletion packages/rich-text/src/component/use-select-object.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,13 @@ export function useSelectObject() {
if ( selection.containsNode( target ) ) return;

const range = ownerDocument.createRange();
// If the target is within a non editable element, select the non
// editable element.
const nodeToSelect = target.isContentEditable
? target
: target.closest( '[contenteditable]' );

range.selectNode( target );
range.selectNode( nodeToSelect );
selection.removeAllRanges();
selection.addRange( range );

Expand Down
4 changes: 4 additions & 0 deletions packages/rich-text/src/to-dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ function getNodeByPath( node, path ) {
}

function append( element, child ) {
if ( child.html !== undefined ) {
return ( element.innerHTML += child.html );
}

mcsf marked this conversation as resolved.
Show resolved Hide resolved
if ( typeof child === 'string' ) {
child = element.ownerDocument.createTextNode( child );
}
Expand Down
8 changes: 6 additions & 2 deletions packages/rich-text/src/to-tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ function fromFormat( {
}

return {
type: formatType.tagName === '*' ? tagName : formatType.tagName,
type: tagName || formatType.tagName,
object: formatType.object,
attributes: restoreOnAttributes( elementAttributes, isEditableTree ),
};
Expand Down Expand Up @@ -326,7 +326,11 @@ export function toTree( {
} )
);

if ( innerHTML ) append( pointer, innerHTML );
if ( innerHTML ) {
append( pointer, {
html: innerHTML,
} );
}
} else {
pointer = append(
getParent( pointer ),
Expand Down
16 changes: 8 additions & 8 deletions test/e2e/specs/editor/various/footnotes.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ test.describe( 'Footnotes', () => {
{
name: 'core/paragraph',
attributes: {
content: `second paragraph<a href="#${ id1 }" id="${ id1 }-link" data-fn="${ id1 }" class="fn">*</a>`,
content: `second paragraph<sup data-fn="${ id1 }" class="fn"><a href="#${ id1 }" id="${ id1 }-link">1</a></sup>`,
},
},
{
Expand All @@ -72,13 +72,13 @@ test.describe( 'Footnotes', () => {
{
name: 'core/paragraph',
attributes: {
content: `first paragraph<a href="#${ id2 }" id="${ id2 }-link" data-fn="${ id2 }" class="fn">*</a>`,
content: `first paragraph<sup data-fn="${ id2 }" class="fn"><a href="#${ id2 }" id="${ id2 }-link">1</a></sup>`,
},
},
{
name: 'core/paragraph',
attributes: {
content: `second paragraph<a href="#${ id1 }" id="${ id1 }-link" data-fn="${ id1 }" class="fn">*</a>`,
content: `second paragraph<sup data-fn="${ id1 }" class="fn"><a href="#${ id1 }" id="${ id1 }-link">2</a></sup>`,
},
},
{
Expand Down Expand Up @@ -106,13 +106,13 @@ test.describe( 'Footnotes', () => {
{
name: 'core/paragraph',
attributes: {
content: `second paragraph<a href="#${ id1 }" id="${ id1 }-link" data-fn="${ id1 }" class="fn">*</a>`,
content: `second paragraph<sup data-fn="${ id1 }" class="fn"><a href="#${ id1 }" id="${ id1 }-link">1</a></sup>`,
},
},
{
name: 'core/paragraph',
attributes: {
content: `first paragraph<a href="#${ id2 }" id="${ id2 }-link" data-fn="${ id2 }" class="fn">*</a>`,
content: `first paragraph<sup data-fn="${ id2 }" class="fn"><a href="#${ id2 }" id="${ id2 }-link">2</a></sup>`,
},
},
{
Expand All @@ -138,7 +138,7 @@ test.describe( 'Footnotes', () => {
{
name: 'core/paragraph',
attributes: {
content: `second paragraph<a href="#${ id1 }" id="${ id1 }-link" data-fn="${ id1 }" class="fn">*</a>`,
content: `second paragraph<sup data-fn="${ id1 }" class="fn"><a href="#${ id1 }" id="${ id1 }-link">1</a></sup>`,
},
},
{
Expand Down Expand Up @@ -202,7 +202,7 @@ test.describe( 'Footnotes', () => {
{
name: 'core/list-item',
attributes: {
content: `1<a href="#${ id1 }" id="${ id1 }-link" data-fn="${ id1 }" class="fn">*</a>`,
content: `1<sup data-fn="${ id1 }" class="fn"><a href="#${ id1 }" id="${ id1 }-link">1</a></sup>`,
},
},
],
Expand Down Expand Up @@ -242,7 +242,7 @@ test.describe( 'Footnotes', () => {
{
cells: [
{
content: `1<a href="#${ id1 }" id="${ id1 }-link" data-fn="${ id1 }" class="fn">*</a>`,
content: `1<sup data-fn="${ id1 }" class="fn"><a href="#${ id1 }" id="${ id1 }-link">1</a></sup>`,
tag: 'td',
},
{
Expand Down
Loading