Skip to content
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 );
}

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