-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Add Inline Images and Inline Blocks API #5794
Changes from 51 commits
b5e6b68
1adb48f
9e21ccc
b7ab81c
2cdd67c
ea93dd7
8232ac0
c1fe71f
01cc53d
7d87bae
97b2f43
36349ed
d5f73cf
e550485
368bfbd
0f61e10
ef7921a
3a124c9
40aef86
f7ae50b
4fbbff8
2349b63
aa5f6fa
75d9f67
c24e877
e13e34d
53a2477
eb212cb
99d78af
0849feb
b2e0feb
bd4fa5a
8d551e1
d42271d
5a453fd
7895d6d
e7485e2
a2967fb
980471c
07c77d2
c931139
60f0582
671b469
caf3342
18522d4
e1b1f4f
0264b4d
29a3833
1184190
f922183
2e5fca4
f4a4358
7d6e6a1
b8a225a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,6 +38,10 @@ | |
width: 100%; | ||
max-height: 100%; | ||
overflow: auto; | ||
|
||
img { | ||
display: inline; | ||
} | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { registerInlineBlockType } from '../inline-blocks'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import * as inlineImage from './inline-image'; | ||
|
||
export const registerCoreInlineBlocks = () => { | ||
[ | ||
inlineImage, | ||
].forEach( ( { name, settings } ) => { | ||
registerInlineBlockType( name, settings ); | ||
} ); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { __ } from '@wordpress/i18n'; | ||
|
||
export const settings = { | ||
id: 'inline-image', | ||
|
||
title: __( 'Inline Image' ), | ||
|
||
type: 'image', | ||
|
||
icon: 'format-image', | ||
|
||
render( { id, url, alt, width } ) { | ||
const imgWidth = width > 150 ? 150 : width; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See const imgWidth = Math.min( 150, width ); There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good call. I noticed @iseulde used There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, I already changed this yes. 😄 |
||
// set width in style attribute to prevent Block CSS from overriding it | ||
const img = `<img class="wp-image-${ id }" style="width:${ imgWidth }px;" src="${ url }" alt="${ alt }" />`; | ||
|
||
return img; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would be interesting if this is a React component and then render to string in RichText? Not sure though. Cc @youknowriad @aduth regarding RichText state and formats. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I think this should be returning an element. If No need to break convention with block There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed. @iseulde added |
||
}, | ||
}; | ||
|
||
export const name = 'core/inline-image'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,21 +16,26 @@ import { withSelect, withDispatch } from '@wordpress/data'; | |
* Internal dependencies | ||
*/ | ||
import InserterMenu from './menu'; | ||
import InserterInlineMenu from './inline-menu'; | ||
|
||
class Inserter extends Component { | ||
constructor() { | ||
super( ...arguments ); | ||
|
||
this.onToggle = this.onToggle.bind( this ); | ||
this.isInsertingInline = this.isInsertingInline.bind( this ); | ||
this.showInsertionPoint = this.showInsertionPoint.bind( this ); | ||
this.hideInsertionPoint = this.hideInsertionPoint.bind( this ); | ||
this.state = { isInline: false }; | ||
} | ||
|
||
onToggle( isOpen ) { | ||
const { onToggle } = this.props; | ||
|
||
if ( isOpen ) { | ||
this.props.showInsertionPoint(); | ||
this.showInsertionPoint(); | ||
} else { | ||
this.props.hideInsertionPoint(); | ||
this.hideInsertionPoint(); | ||
} | ||
|
||
// Surface toggle callback to parent component | ||
|
@@ -39,15 +44,47 @@ class Inserter extends Component { | |
} | ||
} | ||
|
||
showInsertionPoint() { | ||
const { showInlineInsertionPoint, showInsertionPoint } = this.props; | ||
|
||
if ( this.isInsertingInline() ) { | ||
this.setState( { isInline: true } ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need local state here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using local state here prevents the Inline Blocks menu from briefly rendering the standard blocks menu while closing, as described in the second bullet point of #5794 (comment). Perhaps there is a way to solve that issue without using local state though -- I'll think about it. |
||
showInlineInsertionPoint(); | ||
} else { | ||
this.setState( { isInline: false } ); | ||
showInsertionPoint(); | ||
} | ||
} | ||
|
||
hideInsertionPoint() { | ||
const { hideInlineInsertionPoint, hideInsertionPoint } = this.props; | ||
|
||
if ( this.state.isInline ) { | ||
hideInlineInsertionPoint(); | ||
} else { | ||
hideInsertionPoint(); | ||
} | ||
} | ||
|
||
isInsertingInline() { | ||
const { selectedBlock, canInsertInline } = this.props; | ||
|
||
return selectedBlock && | ||
! isUnmodifiedDefaultBlock( selectedBlock ) && | ||
canInsertInline; | ||
} | ||
|
||
render() { | ||
const { | ||
position, | ||
title, | ||
children, | ||
onInsertBlock, | ||
onInsertInline, | ||
hasSupportedBlocks, | ||
isLocked, | ||
} = this.props; | ||
const { isInline } = this.state; | ||
|
||
if ( ! hasSupportedBlocks || isLocked ) { | ||
return null; | ||
|
@@ -74,11 +111,23 @@ class Inserter extends Component { | |
) } | ||
renderContent={ ( { onClose } ) => { | ||
const onSelect = ( item ) => { | ||
onInsertBlock( item ); | ||
if ( isInline ) { | ||
onInsertInline( item.name ); | ||
} else { | ||
onInsertBlock( item ); | ||
} | ||
|
||
onClose(); | ||
}; | ||
|
||
if ( isInline ) { | ||
return ( | ||
<InserterInlineMenu | ||
onSelect={ onSelect } | ||
/> | ||
); | ||
} | ||
|
||
return <InserterMenu onSelect={ onSelect } />; | ||
} } | ||
/> | ||
|
@@ -94,6 +143,7 @@ export default compose( [ | |
getSelectedBlock, | ||
getSupportedBlocks, | ||
getEditorSettings, | ||
isInlineInsertAvailable, | ||
} = select( 'core/editor' ); | ||
const { allowedBlockTypes, templateLock } = getEditorSettings(); | ||
const insertionPoint = getBlockInsertionPoint(); | ||
|
@@ -105,6 +155,7 @@ export default compose( [ | |
selectedBlock: getSelectedBlock(), | ||
hasSupportedBlocks: true === supportedBlocks || ! isEmpty( supportedBlocks ), | ||
isLocked: !! templateLock, | ||
canInsertInline: isInlineInsertAvailable(), | ||
}; | ||
} ), | ||
withDispatch( ( dispatch, ownProps ) => ( { | ||
|
@@ -120,5 +171,8 @@ export default compose( [ | |
} | ||
return dispatch( 'core/editor' ).insertBlock( insertedBlock, index, rootUID ); | ||
}, | ||
showInlineInsertionPoint: dispatch( 'core/editor' ).showInlineInsertionPoint, | ||
hideInlineInsertionPoint: dispatch( 'core/editor' ).hideInlineInsertionPoint, | ||
onInsertInline: dispatch( 'core/editor' ).insertInline, | ||
} ) ), | ||
] )( Inserter ); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { Component, Fragment } from '@wordpress/element'; | ||
import { registerCoreInlineBlocks } from '../../../core-inline-blocks'; | ||
import { getInlineBlockTypes } from '../../../inline-blocks'; | ||
import { __ } from '@wordpress/i18n'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import InserterGroup from './group'; | ||
|
||
// TODO: move this to lib/client-assets.php | ||
registerCoreInlineBlocks(); | ||
|
||
export default class InserterInlineMenu extends Component { | ||
render() { | ||
const inlineBlocks = getInlineBlockTypes(); | ||
|
||
return ( | ||
<Fragment> | ||
<div | ||
className="editor-inserter__separator" | ||
id="editor-inserter__separator-inline" | ||
aria-hidden="true" | ||
> | ||
{ __( 'Inline Blocks' ) } | ||
</div> | ||
<InserterGroup | ||
items={ inlineBlocks } | ||
onSelectItem={ this.props.onSelect } | ||
/> | ||
</Fragment> | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -39,6 +39,7 @@ import Autocomplete from '../autocomplete'; | |
import BlockFormatControls from '../block-format-controls'; | ||
import FormatToolbar from './format-toolbar'; | ||
import TinyMCE from './tinymce'; | ||
import InlineBlocks from './inline-blocks'; | ||
import { pickAriaProps } from './aria'; | ||
import patterns from './patterns'; | ||
import { EVENTS } from './constants'; | ||
|
@@ -122,6 +123,8 @@ export class RichText extends Component { | |
this.onPaste = this.onPaste.bind( this ); | ||
this.onCreateUndoLevel = this.onCreateUndoLevel.bind( this ); | ||
this.setFocusedElement = this.setFocusedElement.bind( this ); | ||
this.getFocusPosition = this.getFocusPosition.bind( this ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remnant? Cannot see this in this diff. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for catching that. Removed in f4a4358. |
||
this.getInsertPosition = this.getInsertPosition.bind( this ); | ||
|
||
this.state = { | ||
formats: {}, | ||
|
@@ -439,6 +442,18 @@ export class RichText extends Component { | |
}; | ||
} | ||
|
||
getInsertPosition() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this not part of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had left if there since the |
||
// The container is relatively positioned. | ||
const containerPosition = this.containerRef.current.getBoundingClientRect(); | ||
const rect = getRectangleFromRange( this.editor.selection.getRng() ); | ||
|
||
return { | ||
top: rect.top - containerPosition.top, | ||
left: rect.right - containerPosition.left, | ||
height: rect.height, | ||
}; | ||
} | ||
|
||
/** | ||
* Handles a keydown event from tinyMCE. | ||
* | ||
|
@@ -723,9 +738,12 @@ export class RichText extends Component { | |
} | ||
|
||
componentDidUpdate( prevProps ) { | ||
if ( ! this.editor ) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like this can be moved back to minimise diff. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in f4a4358 |
||
return; | ||
} | ||
|
||
// The `savedContent` var allows us to avoid updating the content right after an `onChange` call | ||
if ( | ||
!! this.editor && | ||
this.props.tagName === prevProps.tagName && | ||
this.props.value !== prevProps.value && | ||
this.props.value !== this.savedContent && | ||
|
@@ -871,6 +889,12 @@ export class RichText extends Component { | |
{ formatToolbar } | ||
</div> | ||
) } | ||
{ isSelected && | ||
<InlineBlocks | ||
editor={ this.editor } | ||
getInsertPosition={ this.getInsertPosition } | ||
/> | ||
} | ||
<Autocomplete onReplace={ this.props.onReplace } completers={ autocompleters }> | ||
{ ( { isExpanded, listBoxId, activeId } ) => ( | ||
<Fragment> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any way we could somehow have a "front-end" style sheet in the inline block registrations that gets included/imported here? Probably thinking too much of SCSS as JS haha. Would be cool if we could just do
or something. Ignore me I know nothing about SCSS 🙈
Maybe @jasmussen knows some magic here to separate the styles.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ooooooh, something like this maybe? https://sass-lang.com/guide#topic-6
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not entirely sure about the context for this code, but if the goal is to reuse bits of CSS then yes, I think a mixin can be the right way to do it. We already do this for button hover/focus styles: https://github.com/WordPress/gutenberg/blob/master/edit-post/assets/stylesheets/_mixins.scss#L129
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, these would be the styles from all inline blocks together. Not sure about the best solution here, maybe the current one is good for a first version.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting thoughts here. I like the idea of using a mixin, but not sure if it's possible to combine a group of stylesheets into a single mixin. Perhaps examining how standard blocks add styles would be instructive here. It doesn't look like they use mixins, but instead each have their own stylesheets (Image block for example). Blocks are also given a unique className -- maybe inline blocks could have a className property as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is fine to figure out later.