-
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 CodeMirror to the HTML block #4348
Merged
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
3269e1c
Use CodeMirror in Custom HTML block
noisysocks c78dc36
Prevent CodeEditor from propagating events
noisysocks a2cfe2e
Allow CodeEditor to be focused via the `focus` prop
noisysocks f28b685
Improve HTML block styling
noisysocks d7305dc
Allow HTML block to be selected when it's in preview mode
noisysocks 40d12e4
Always focus the editor after React has finished painting
noisysocks 6206f13
Change CodeEditor test to use a snapshot test
noisysocks 67cbf3d
Increase code editor padding in HTML block
noisysocks bc4f047
Increase z-index of block toolbar elements
noisysocks 28c925a
Improve HTML block cursor behaviour
noisysocks 3119677
Lazily load CodeMirror assets
noisysocks 30ade66
Fix typos in CodeEditor README
noisysocks File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,25 @@ | ||
.wp-block-html.blocks-plain-text { | ||
font-family: $editor-html-font; | ||
font-size: $text-editor-font-size; | ||
color: $dark-gray-800; | ||
padding: .8em 1.6em; | ||
overflow-x: auto !important; | ||
border: 1px solid $light-gray-500; | ||
border-radius: 4px; | ||
.gutenberg .wp-block-html { | ||
iframe { | ||
display: block; | ||
|
||
// Disable pointer events so that we can click on the block to select it | ||
pointer-events: none; | ||
} | ||
|
||
.CodeMirror { | ||
border-radius: 4px; | ||
border: 1px solid $light-gray-500; | ||
font-family: $editor-html-font; | ||
font-size: $text-editor-font-size; | ||
height: auto; | ||
} | ||
|
||
.CodeMirror-gutters { | ||
background: $white; | ||
border-right: none; | ||
} | ||
|
||
.CodeMirror-lines { | ||
padding: 8px 8px 8px 0; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,22 @@ | ||
// Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
||
exports[`core/html block edit matches snapshot 1`] = ` | ||
<textarea | ||
aria-label="HTML" | ||
class="blocks-plain-text wp-block-html" | ||
rows="1" | ||
/> | ||
<div | ||
class="wp-block-html" | ||
> | ||
<div | ||
class="components-placeholder" | ||
> | ||
<div | ||
class="components-placeholder__label" | ||
/> | ||
<div | ||
class="components-placeholder__fieldset" | ||
> | ||
<span | ||
class="spinner is-active" | ||
/> | ||
</div> | ||
</div> | ||
</div> | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
CodeEditor | ||
======= | ||
|
||
CodeEditor is a React component that provides the user with a code editor | ||
that has syntax highlighting and linting. | ||
|
||
The components acts as a drop-in replacement for a <textarea>, and uses the | ||
CodeMirror library that is provided as part of WordPress Core. | ||
|
||
## Usage | ||
|
||
```jsx | ||
import { CodeEditor } from '@wordpress/components'; | ||
|
||
function editCode() { | ||
return ( | ||
<CodeEditor | ||
value={ '<p>This is some <b>HTML</b> code that will have syntax highlighting!</p>' } | ||
onChange={ value => console.log( value ) } | ||
/> | ||
); | ||
} | ||
``` | ||
|
||
## Props | ||
|
||
The component accepts the following props: | ||
|
||
### value | ||
|
||
The source code to load into the code editor. | ||
|
||
- Type: `string` | ||
- Required: Yes | ||
|
||
### focus | ||
|
||
Whether or not the code editor should be focused. | ||
|
||
- Type: `boolean` | ||
- Required: No | ||
|
||
### onFocus | ||
|
||
The function called when the editor is focused. | ||
|
||
- Type: `Function` | ||
- Required: No | ||
|
||
### onChange | ||
|
||
The function called when the user has modified the source code via the | ||
editor. It is passed the new value as an argument. | ||
|
||
- Type: `Function` | ||
- Required: No |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { Component } from '@wordpress/element'; | ||
import { keycodes } from '@wordpress/utils'; | ||
|
||
/** | ||
* Module constants | ||
*/ | ||
const { UP, DOWN } = keycodes; | ||
|
||
class CodeEditor extends Component { | ||
constructor() { | ||
super( ...arguments ); | ||
|
||
this.onFocus = this.onFocus.bind( this ); | ||
this.onBlur = this.onBlur.bind( this ); | ||
this.onCursorActivity = this.onCursorActivity.bind( this ); | ||
this.onKeyHandled = this.onKeyHandled.bind( this ); | ||
} | ||
|
||
componentDidMount() { | ||
const instance = wp.codeEditor.initialize( this.textarea, window._wpGutenbergCodeEditorSettings ); | ||
this.editor = instance.codemirror; | ||
|
||
this.editor.on( 'focus', this.onFocus ); | ||
this.editor.on( 'blur', this.onBlur ); | ||
this.editor.on( 'cursorActivity', this.onCursorActivity ); | ||
this.editor.on( 'keyHandled', this.onKeyHandled ); | ||
|
||
this.updateFocus(); | ||
} | ||
|
||
componentDidUpdate( prevProps ) { | ||
if ( this.props.value !== prevProps.value && this.editor.getValue() !== this.props.value ) { | ||
this.editor.setValue( this.props.value ); | ||
} | ||
|
||
if ( this.props.focus !== prevProps.focus ) { | ||
this.updateFocus(); | ||
} | ||
} | ||
|
||
componentWillUnmount() { | ||
this.editor.on( 'focus', this.onFocus ); | ||
this.editor.off( 'blur', this.onBlur ); | ||
this.editor.off( 'cursorActivity', this.onCursorActivity ); | ||
this.editor.off( 'keyHandled', this.onKeyHandled ); | ||
|
||
this.editor.toTextArea(); | ||
this.editor = null; | ||
} | ||
|
||
onFocus() { | ||
if ( this.props.onFocus ) { | ||
this.props.onFocus(); | ||
} | ||
} | ||
|
||
onBlur( editor ) { | ||
if ( this.props.onChange ) { | ||
this.props.onChange( editor.getValue() ); | ||
} | ||
} | ||
|
||
onCursorActivity( editor ) { | ||
this.lastCursor = editor.getCursor(); | ||
} | ||
|
||
onKeyHandled( editor, name, event ) { | ||
/* | ||
* Pressing UP/DOWN should only move focus to another block if the cursor is | ||
* at the start or end of the editor. | ||
* | ||
* We do this by stopping UP/DOWN from propagating if: | ||
* - We know what the cursor was before this event; AND | ||
* - This event caused the cursor to move | ||
*/ | ||
if ( event.keyCode === UP || event.keyCode === DOWN ) { | ||
const areCursorsEqual = ( a, b ) => a.line === b.line && a.ch === b.ch; | ||
if ( this.lastCursor && ! areCursorsEqual( editor.getCursor(), this.lastCursor ) ) { | ||
event.stopImmediatePropagation(); | ||
} | ||
} | ||
} | ||
|
||
updateFocus() { | ||
if ( this.props.focus && ! this.editor.hasFocus() ) { | ||
// Need to wait for the next frame to be painted before we can focus the editor | ||
window.requestAnimationFrame( () => { | ||
this.editor.focus(); | ||
} ); | ||
} | ||
|
||
if ( ! this.props.focus && this.editor.hasFocus() ) { | ||
document.activeElement.blur(); | ||
} | ||
} | ||
|
||
render() { | ||
return <textarea ref={ ref => ( this.textarea = ref ) } value={ this.props.value } />; | ||
} | ||
} | ||
|
||
export default CodeEditor; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 love this.