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: Add (foot)notes #15267

Closed
wants to merge 12 commits into from
Closed
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
24 changes: 24 additions & 0 deletions docs/designers-developers/developers/data/data-core-editor.md
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,18 @@ _Related_

- getFirstMultiSelectedBlockClientId in core/block-editor store.

<a name="getFootnotes" href="#getFootnotes">#</a> **getFootnotes**
Copy link
Member

@aduth aduth Jul 24, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this something we could use meta / custom sources for (#16402), rather than to build-in awareness of footnotes to the editor module?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a super deep understanding of custom sources. Would it work if the source is the post content?


Returns the post footnotes.

_Parameters_

- _state_ `Object`: Editor state.

_Returns_

- `Object`: The footnotes.

<a name="getGlobalBlockCount" href="#getGlobalBlockCount">#</a> **getGlobalBlockCount**

_Related_
Expand Down Expand Up @@ -1400,6 +1412,18 @@ _Related_

Undocumented declaration.

<a name="updateFootnotes" href="#updateFootnotes">#</a> **updateFootnotes**

Returns an action object used in signalling that the footnotes should be updated.

_Parameters_

- _footnotes_ `Object`: The footnotes.

_Returns_

- `Object`: Action object.

<a name="updatePost" href="#updatePost">#</a> **updatePost**

Returns an action object used in signalling that a patch of updates for the
Expand Down
20 changes: 20 additions & 0 deletions lib/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,23 @@ function enqueue_editor_block_styles_assets() {
wp_enqueue_script( 'wp-block-styles' );
}
add_action( 'enqueue_block_editor_assets', 'enqueue_editor_block_styles_assets' );

/**
* Adds the `footnotes` block category.
*
* @param string $categories The categories to filter.
*
* @return string The filtered categories.
*/
function gutenberg_add_block_categories( $categories ) {
array_push(
$categories,
array(
'slug' => 'footnotes',
'title' => __( 'Footnotes', 'gutenberg' ),
)
);

return $categories;
}
add_filter( 'block_categories', 'gutenberg_add_block_categories' );
3 changes: 3 additions & 0 deletions packages/block-editor/src/components/block-list/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ class BlockList extends Component {
hasMultiSelection,
renderAppender,
enableAnimation,
children,
} = this.props;

return (
Expand Down Expand Up @@ -242,6 +243,8 @@ class BlockList extends Component {
rootClientId={ rootClientId }
renderAppender={ renderAppender }
/>

{ children }
</div>
);
}
Expand Down
2 changes: 2 additions & 0 deletions packages/block-editor/src/components/rich-text/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,8 @@ RichTextContainer.Content.defaultProps = {
value: '',
};

RichTextContainer.Bare = RichTextWrapper;

/**
* @see https://github.com/WordPress/gutenberg/blob/master/packages/block-editor/src/components/rich-text/README.md
*/
Expand Down
1 change: 1 addition & 0 deletions packages/block-library/src/editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
@import "./cover/editor.scss";
@import "./embed/editor.scss";
@import "./file/editor.scss";
@import "./footnotes/editor.scss";
@import "./classic/editor.scss";
@import "./gallery/editor.scss";
@import "./group/editor.scss";
Expand Down
9 changes: 9 additions & 0 deletions packages/block-library/src/footnotes/block.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "core/footnotes",
"category": "footnotes",
"attributes": {
"footnotes": {
"type": "array"
}
}
}
114 changes: 114 additions & 0 deletions packages/block-library/src/footnotes/edit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/**
* External dependencies
*/
import classnames from 'classnames';

/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { IconButton, Toolbar, Slot } from '@wordpress/components';
import { useRef, useState } from '@wordpress/element';
import { useDispatch } from '@wordpress/data';
import { withSafeTimeout } from '@wordpress/compose';
import { RichText, BlockFormatControls } from '@wordpress/block-editor';

function Edit( { attributes, setAttributes, className, setTimeout } ) {
const { footnotes } = attributes;
const ref = useRef( null );
const { clearSelectedBlock } = useDispatch( 'core/block-editor' );
const [ selected, select ] = useState( false );

if ( ! footnotes.length ) {
return null;
}

const hasSelection = footnotes.some( ( { isSelected } ) => isSelected );
const viewAll = () => {
ref.current.focus();
clearSelectedBlock();
// Wait for footnotes to be recalculated after the DOM change.
setTimeout( () =>
// Wait for DOM to update.
setTimeout( () => ref.current.scrollIntoView() )
);
};

const onFocus = () => {
clearSelectedBlock();
select( true );
};

const onBlur = () => {
select( false );
};

return (
<div
className={ classnames( 'wp-block block-library-list', className, {
'is-selected': hasSelection,
} ) }
tabIndex="0"
onFocus={ onFocus }
onBlur={ onBlur }
ref={ ref }
>
{ hasSelection &&
<>
<Toolbar>
<IconButton icon="editor-ol" onClick={ viewAll }>
{ __( 'View all Footnotes' ) }
</IconButton>
<Slot name="__unstable-footnote-controls" />
</Toolbar>
{ selected && <BlockFormatControls.Slot /> }
</>
}
<ol>
{ footnotes.map( ( { id, text, isSelected }, index ) =>
<li
key={ id }
id={ id }
className={ classnames( { 'is-selected': isSelected } ) }
>
{ /* Needed for alignment. */ }
<div>
<a
href={ `#${ id }-anchor` }
aria-label={ __( 'Back to content' ) }
onClick={ () => {
// This is a hack to get the target to focus.
// The attribute will later be removed when selection is set.
document.getElementById( `${ id }-anchor` ).contentEditable = 'false';
} }
>
</a>
{ ' ' }
<RichText.Bare
value={ text }
onChange={ ( value ) => {
setAttributes( {
footnotes: footnotes.map( ( footnote, i ) => {
if ( i !== index ) {
return footnote;
}

return {
...footnote,
text: value,
};
} ),
} );
} }
placeholder={ __( 'Footnote' ) }
/>
</div>
</li>
) }
</ol>
</div>
);
}

export default withSafeTimeout( Edit );
54 changes: 54 additions & 0 deletions packages/block-library/src/footnotes/editor.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
@keyframes slide-in-note {
from {
transform: translateY(100%);
}

to {
transform: translateY(0);
}
}

.editor-styles-wrapper .wp-block-footnotes {
margin: 0;

.components-toolbar {
border: none;
padding: 10px;
}

&.is-selected {
background: #fff;
position: sticky;
bottom: 0;
z-index: 1000;
animation-duration: 0.2s;
animation-name: slide-in-note;
animation-timing-function: ease;
box-shadow: -3px 0 0 0 #555d66;
margin: 0 1px;
border-top: 1px solid rgba(66, 88, 99, 0.4);

// Vertically center the single list item.
ol {
margin-top: 0;
margin-bottom: 0;
padding-bottom: 10px;
}

// We cannot use display as it will affect the numbering.
li {
visibility: hidden;
height: 0;
}

li.is-selected {
visibility: visible;
height: auto;
}
}

li > div > a {
float: left;
margin-right: 5px;
}
}
24 changes: 24 additions & 0 deletions packages/block-library/src/footnotes/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import metadata from './block.json';
import save from './save';
import edit from './edit';

const { name } = metadata;

export { metadata, name };

export const settings = {
title: __( 'Footnotes' ),
supports: {
inserter: false,
},
save,
edit,
};
29 changes: 29 additions & 0 deletions packages/block-library/src/footnotes/save.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';

export default function Save( { attributes } ) {
const { footnotes } = attributes;

if ( ! footnotes.length ) {
return null;
}

return (
<ol>
{ footnotes.map( ( { id, text } ) =>
<li key={ id }>
<a
id={ id }
href={ `#${ id }-anchor` }
aria-label={ __( 'Back to content' ) }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be tricky until we have better validation for blocks if a user edits the post with a different locale loaded. Until we get there, how realistically can we a) hardcode a string in English (in Esperanto?) instead of passing it through __; or b) can we drop the aria-label attribute? More interestingly, is there any accessible standard or convention that indicates the notion of "returning" that is language-independent?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's annoying... Emoji? Are they read and translated?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was also a reason against saving the footnotes in the post content, so it can be generated in PHP.

>
</a>
{ ` ${ text }` }
</li>
) }
</ol>
);
}
15 changes: 15 additions & 0 deletions packages/block-library/src/footnotes/style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Ideally, this should be the post wrapper element. So `.post` or similar.
body {
counter-reset: footnotes;
}

.footnote-anchor {
counter-increment: footnotes;
}

.footnote-anchor::after {
margin-left: 2px;
content: "[" counter(footnotes) "]";
vertical-align: super;
font-size: smaller;
}
2 changes: 2 additions & 0 deletions packages/block-library/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import * as column from './column';
import * as cover from './cover';
import * as embed from './embed';
import * as file from './file';
import * as footnotes from './footnotes';
import * as html from './html';
import * as mediaText from './media-text';
import * as navigationMenu from './navigation-menu';
Expand Down Expand Up @@ -115,6 +116,7 @@ export const registerCoreBlocks = () => {
...embed.common,
...embed.others,
file,
footnotes,
group,
window.wp && window.wp.oldEditor ? classic : null, // Only add the classic block in WP Context
html,
Expand Down
1 change: 1 addition & 0 deletions packages/block-library/src/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
@import "./cover/style.scss";
@import "./embed/style.scss";
@import "./file/style.scss";
@import "./footnotes/style.scss";
@import "./gallery/style.scss";
@import "./image/style.scss";
@import "./latest-comments/style.scss";
Expand Down
1 change: 1 addition & 0 deletions packages/blocks/src/store/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export const DEFAULT_CATEGORIES = [
{ slug: 'widgets', title: __( 'Widgets' ) },
{ slug: 'embed', title: __( 'Embeds' ) },
{ slug: 'reusable', title: __( 'Reusable Blocks' ) },
{ slug: 'footnotes', title: __( 'Footnotes' ) },
];

/**
Expand Down
6 changes: 6 additions & 0 deletions packages/e2e-tests/fixtures/block-transforms.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,12 @@ export const EXPECTED_TRANSFORMS = {
'Group',
],
},
core__footnotes: {
originalBlock: 'Footnotes',
availableTransforms: [
'Group',
],
},
core__gallery: {
originalBlock: 'Gallery',
availableTransforms: [
Expand Down
3 changes: 3 additions & 0 deletions packages/e2e-tests/fixtures/blocks/core__footnotes.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<!-- wp:footnotes {"footnotes":[{"id":"0","text":"test"}]} -->
<ol class="wp-block-footnotes"><li><a id="0" href="#0-anchor" aria-label="Back to content">↑</a> test</li></ol>
<!-- /wp:footnotes -->
Loading