-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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/549 list block indent buttons #717
Changes from 19 commits
40472da
b19ee7f
2418990
d5c0d0b
5f2c3d0
390e99a
ba33927
8dfdd74
1d93fbc
4cc20bf
f60e11d
abb531f
17a5168
1104141
27e1c0f
7156082
f8cce5a
befe07b
528f088
05288e1
68ab9ee
8e36e05
9c5b029
9ed5cf4
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 |
---|---|---|
|
@@ -2,7 +2,7 @@ | |
* External dependencies | ||
*/ | ||
import classnames from 'classnames'; | ||
import { last, isEqual, capitalize, omitBy, forEach, merge } from 'lodash'; | ||
import { last, isEqual, capitalize, omitBy, forEach, merge, identity } from 'lodash'; | ||
import { nodeListToReact } from 'dom-react'; | ||
import { Fill } from 'react-slot-fill'; | ||
import 'element-closest'; | ||
|
@@ -67,6 +67,7 @@ export default class Editable extends wp.element.Component { | |
super( ...arguments ); | ||
|
||
this.onInit = this.onInit.bind( this ); | ||
this.onConfig = this.onConfig.bind( this ); | ||
this.onSetup = this.onSetup.bind( this ); | ||
this.onChange = this.onChange.bind( this ); | ||
this.onNewBlock = this.onNewBlock.bind( this ); | ||
|
@@ -84,6 +85,13 @@ export default class Editable extends wp.element.Component { | |
}; | ||
} | ||
|
||
onConfig( settings ) { | ||
return ( this.props.onConfig || identity )( { | ||
...settings, | ||
forced_root_block: this.props.inline ? false : 'p', | ||
} ); | ||
} | ||
|
||
onSetup( editor ) { | ||
this.editor = editor; | ||
editor.on( 'init', this.onInit ); | ||
|
@@ -93,6 +101,10 @@ export default class Editable extends wp.element.Component { | |
editor.on( 'nodechange', this.onNodeChange ); | ||
editor.on( 'keydown', this.onKeyDown ); | ||
editor.on( 'selectionChange', this.onSelectionChange ); | ||
|
||
if ( this.props.onSetup ) { | ||
this.props.onSetup( editor ); | ||
} | ||
} | ||
|
||
onInit() { | ||
|
@@ -273,6 +285,10 @@ export default class Editable extends wp.element.Component { | |
const focusPosition = this.getRelativePosition( element ); | ||
const bookmark = this.editor.selection.getBookmark( 2, true ); | ||
this.setState( { alignment, bookmark, formats, focusPosition } ); | ||
|
||
if ( this.props.onNodeChange ) { | ||
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. We can safely remove this, as in your implementation we've exposed the 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. Done. |
||
this.props.onNodeChange( { element, parents } ); | ||
} | ||
} | ||
|
||
updateContent() { | ||
|
@@ -394,7 +410,6 @@ export default class Editable extends wp.element.Component { | |
className, | ||
showAlignments = false, | ||
inlineToolbar = false, | ||
inline, | ||
formattingControls, | ||
placeholder, | ||
} = this.props; | ||
|
@@ -438,12 +453,10 @@ export default class Editable extends wp.element.Component { | |
|
||
<TinyMCE | ||
tagName={ tagName } | ||
onConfig={ this.onConfig } | ||
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. Since we only ever use this in Thinking something like: let settings = {
forced_root_block: this.props.inline ? false : 'p'
};
if ( this.props.onConfig ) {
settings = this.props.onConfig( settings );
}
return (
<TinyMCE
settings={ settings }
{ /* ... */ }
/>
); 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 idea. I will do that. 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. Hmm, the problem is that without the callback we have to put any code for merging settings (ie for example the list of plugins loaded) into the TinyMCE block. The callback is rare anyway so it's not worth optimizing. 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. Either that or the default TinyMCE settings would have to be some sort of constant accessible outside of the block so the wrapping blocks can handle the settings merge. I am not sure how to go about doing that. |
||
onSetup={ this.onSetup } | ||
style={ style } | ||
defaultValue={ value } | ||
settings={ { | ||
forced_root_block: inline ? false : 'p', | ||
} } | ||
isEmpty={ this.state.empty } | ||
placeholder={ placeholder } | ||
key={ key } /> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,40 @@ import Editable from '../../editable'; | |
|
||
const { children, prop } = hpq; | ||
|
||
function execCommand( command ) { | ||
return ( { editor } ) => { | ||
if ( editor ) { | ||
editor.execCommand( command ); | ||
} | ||
}; | ||
} | ||
|
||
function listIsActive( listType ) { | ||
return ( { nodeName = 'OL', internalListType } ) => { | ||
return listType === ( internalListType ? internalListType : nodeName ); | ||
}; | ||
} | ||
|
||
function listSetType( listType, editorCommand ) { | ||
return ( { internalListType, editor }, setAttributes ) => { | ||
if ( internalListType ) { | ||
// only change list types, don't toggle off internal lists | ||
if ( internalListType !== listType ) { | ||
if ( editor ) { | ||
editor.execCommand( editorCommand ); | ||
} | ||
} | ||
} else { | ||
setAttributes( { nodeName: listType } ); | ||
} | ||
}; | ||
} | ||
|
||
function findInternalListType( { parents } ) { | ||
const list = parents.find( ( node ) => node.nodeName === 'UL' || node.nodeName === 'OL' ); | ||
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. We don't currently use a polyfill for prototype methods on base object types, so ...which is all to say, this will probably be fine, but cause Internet Explorer to fail for some time. Alternative is to use 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. Ok, I changed it to use the lodash find. |
||
return list ? list.nodeName : null; | ||
} | ||
|
||
registerBlock( 'core/list', { | ||
title: wp.i18n.__( 'List' ), | ||
icon: 'editor-ul', | ||
|
@@ -26,18 +60,24 @@ registerBlock( 'core/list', { | |
{ | ||
icon: 'editor-ul', | ||
title: wp.i18n.__( 'Convert to unordered' ), | ||
isActive: ( { nodeName = 'OL' } ) => nodeName === 'UL', | ||
onClick( attributes, setAttributes ) { | ||
setAttributes( { nodeName: 'UL' } ); | ||
}, | ||
isActive: listIsActive( 'UL' ), | ||
onClick: listSetType( 'UL', 'InsertUnorderedList' ), | ||
}, | ||
{ | ||
icon: 'editor-ol', | ||
title: wp.i18n.__( 'Convert to ordered' ), | ||
isActive: ( { nodeName = 'OL' } ) => nodeName === 'OL', | ||
onClick( attributes, setAttributes ) { | ||
setAttributes( { nodeName: 'OL' } ); | ||
}, | ||
isActive: listIsActive( 'OL' ), | ||
onClick: listSetType( 'OL', 'InsertOrderedList' ), | ||
}, | ||
{ | ||
icon: 'editor-outdent', | ||
title: wp.i18n.__( 'Outdent list item' ), | ||
onClick: execCommand( 'Outdent' ), | ||
}, | ||
{ | ||
icon: 'editor-indent', | ||
title: wp.i18n.__( 'Indent list item' ), | ||
onClick: execCommand( 'Indent' ), | ||
}, | ||
], | ||
|
||
|
@@ -72,6 +112,17 @@ registerBlock( 'core/list', { | |
return ( | ||
<Editable | ||
tagName={ nodeName.toLowerCase() } | ||
onConfig={ ( settings ) => ( { | ||
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 like that this is immutable, but it does seem a bit odd to have an Or, and forgive me again for my extreme pondering, explore functions as children as an interesting extensibility option for overriding the default TinyMCE rendering to extend settings. Something like: <Editable
{ /* ... */ }
>
( tinyMCEProps ) => (
<TinyMCE
{ ...tinyMCEProps }
settings={
...tinyMCEProps.settings,
plugins: ( settings.plugins || [] ).concat( 'lists' ),
lists_indent_on_tab: false,
}
/>
)
</Editable> 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 changed onConfig() to getSettings() . I don't understand what you are trying to explain with the code snippet. |
||
...settings, | ||
plugins: ( settings.plugins || [] ).concat( 'lists' ), | ||
lists_indent_on_tab: false, | ||
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 know you noted this in the original pull request description. It did catch me off guard though; I'd tried tabbing to indent and the screen shifted to focus the next block. I don't know that I have a good answer here. Perhaps @jasmussen could weigh in to UX implications, but we could keep this as-is for now to at least allow consistent tab behavior. 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. In normal TinyMCE you can escape the list by using the arrow keys and then tabbing works as normal. The problem is that because TinyMCE does not extend out of the list in this case you can't escape the list block and hence it is impossible to navigate away only using the keyboard. It is a tricky problem, possibly another keybinding could be used for indenting or possibly the arrow keys could be used for inter-block navigation when you are on the first/last line. |
||
} ) } | ||
onSetup={ ( editor ) => { | ||
editor.on( 'nodeChange', ( nodeInfo ) => { | ||
setAttributes( { internalListType: findInternalListType( nodeInfo ) } ); | ||
} ); | ||
setAttributes( { 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. I haven't really settled on whether we should encourage attributes as a generic object store. One issue and potential reason for not leaning on it heavily is that the default behavior of serializing block comments is to encode any additional attributes outside a block's own <!-- wp:core/list editor="[object Object]" internalListType="null" --> The proposed block API includes an What do you think? We could leave this for now and address separately. This is also why I'd considered using 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'll leave it for now. I was unaware that state was being serialized beyond what I was outputting in save. I think it should be opt-in. |
||
} } | ||
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. Tab snuck in instead of space between curly braces. 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. Hmmm, I have my editor set to use spaces but the the ESLint plugin converts them to tabs. I suspect that in this case it was split over multiple lines and I collapsed it to one line - so the tab got where it shouldn't. |
||
onChange={ ( nextValues ) => { | ||
setAttributes( { values: nextValues } ); | ||
} } | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,6 @@ | ||
.blocks-list .blocks-editable__tinymce { | ||
.blocks-list .blocks-editable__tinymce, | ||
.blocks-list .blocks-editable__tinymce ul, | ||
.blocks-list .blocks-editable__tinymce ol { | ||
padding-left: 2.5em; | ||
margin-left: 0; | ||
} |
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.
Should our
forced_root_block
always take precedence over the block's own? I don't really feel strongly either way, just seems like a block overriding settings might want ability to override anything.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 just kept the existing behaviour.