Draft.js as the editor


#1

So I assume you all heard about Draft.js.

Is there any chance of using that as the edit component in Atom? With rich text, code could look a lot better, with bigger fonts for function definitions, each code block in a div with a border, JSON code rendering in a tree, inline graphs about test results…


#2

No, but it looks pretty cool.

Sure, why not. You’d have to build an Atom package that uses the draft components. It seems to me like a perfect fit since it uses the DOM and Atom is based on the DOM. The effort involved would mainly be a function of how easy the Draft.js API is to use.

It wouldn’t use any of the existing Atom texteditor code. You would start of with a blank DOM (see package html-tab by me) in a tab and put the Draft.js building blocks in it. Now that I think about it, it would be pretty easy to do.

Why don’t you take a stab at putting it together? We could help with the design and coding.


#3

Well, I’ll have a look. Seems like fun :slight_smile:


#4

I didn’t get very far and I hit bedtime. This is what I have so far, basically I cloned html-tab, installed react, react-dom and draft-js with npm, and changed the html-tab-view to be


# lib/html-tab-view
React = require 'react'
E = React.createElement

ReactDom = require 'react-dom'
{Editor} = require 'draft-js'

{View} = require 'atom-space-pen-views'

class MyEditor extends React.Component
  constructor: (props) ->
    super props
    @onChange = (editorState) => @setState({editorState})

  render: ->
    {editorState} = @state
    E Editor, {editorState, onChange: @onChange}


module.exports =
class HtmlTabView extends View

  @content: ->
    @div =>
      @h1 "The html-tab package is Alive! It's ALIVE!"
      @button onClick: 'load', "click to start"
      @div outlet: 'draft'

  @load: ->
    ReactDom.render (E MyEditor), @draft

When I run that, it loads the view and when I click the button, it complains about:

index.html:1 Refused to execute inline event handler because it violates the following Content Security Policy directive: "script-src 'self'". Either the 'unsafe-inline' keyword, a hash ('sha256-...'), or a nonce ('nonce-...') is required to enable inline execution.

Which looks like something I need to allow somewhere?


#5

Check out Atom Content Security Policy Error

P.S. There has been discussion about whether you can use React in Atom. Search for that.


#6

The essence of the discussion is that, at least at the time the Atom team was evaluating it, React didn’t behave well if more than one version of React was loaded at the same time. For example, if the Atom core components were using version X and a package was using Y.


#7

Yes, it was quite a while ago.

Also, is core using react anymore? I don’t think so. So then the question is if two packages were to use react could they conflict? That could be easier to solve. The authors could cooperate to use the same version of react.

Also also, facebook might have fixed the react problem.


#8

No, core isn’t using React anymore.

That’s entirely possible too :grinning:


#9

Yes, that hasn’t changed much, but it looks like the next version will improve matters. The two issues around it have also been closed since v0.14.

The new version will remove the data-ids from DOM elements, and it will also introduce a stable API that will get deprecations before removals, so you should be able to use a package written for version x together with one written for version x+1.


#10

I’m not sure that x vs x+1 was the real concern … it was more like, x vs x+14 :grinning:


#11

:slight_smile: Well I’m not 100% certain, but it looks that by removing the data-id and instead relying on an internal map, two versions of React won’t know of each other’s existence, and as long as they don’t touch each other’s DOM (which they shouldn’t do anyway), things will be fine.

The x+1 thing is more for if Atom ships with a single React version that plugins would need to use.


#12

So I took a look at that and I’m a bit stuck, I’m not sure how to work around that.

I’m fairly sure the error actually happens in space-pen, due to the callback on the button. If I add a console.log before the React mount it doesn’t show so the mount hasn’t happened yet. Any quick pointers on that?


#13

I just realized the CSP error is on index.html:1. What is index.html?


#14

No idea, I didn’t create one. I assumed it was a product of spacepen?


#15

I think we need to know what that line 1 looks like. It might be line 1 because it is uglified.

Or I’m on a wild goose chase. Just turning off all security crap would be best. It doesn’t mean anything in the context of Atom.

Search Atom/Atom issues and if you don’t find anything post an issue.


#16

It doesn’t seem to exist - when I click on the error line, I go to file:///Applications/Atom.app/Contents/Resources/app.asar/static/index.html and there is no such file.

A candidate for the contents would be https://github.com/atom/atom/blob/41161dd462a1be9073752a34b4809612f933da6e/static/index.html with relevant commit https://github.com/atom/atom/commit/5e8213d45ff0fa3390e4871095e381eb6afb4537.


#17

You’re almost there. Have you tried using the atom.allowUnsafeEval method? Try sticking it different places. This goes back to the original thread about CSP. Apparently using inline code is the same as an eval.


#18

Yup:


# lib/html-tab-view
React = require 'react'
E = React.createElement

ReactDom = require 'react-dom'
{Editor} = require 'draft-js'

{View} = require 'atom-space-pen-views'
{allowUnsafeEval} = require 'loophole'

console.log 'loading'

class MyEditor extends React.Component
  constructor: (props) ->
    super props
    @onChange = allowUnsafeEval (editorState) => @setState({editorState})

  render: ->
    {editorState} = @state
    E Editor, {editorState, onChange: @onChange}


module.exports =
class HtmlTabView extends View

  @content: ->
    @div =>
      @h1 "The html-tab package is Alive! It's ALIVE!"
      @button onClick: (allowUnsafeEval =>
        console.log 'hi', @
        ReactDom.render (E MyEditor), (@draft.get 0)
      ), "click to start"
      @div outlet: 'draft'

  @load: ->
    ReactDom.render (E MyEditor), @draft

it actually runs the ‘hi’ console log, but using @draft to get a reference to the draft div fails. Spacepen?


#19

actually that also fails, it was running the script immediately instead of on click. I tried allowUnsafeEval => () => ... but that also got the CSP error.

Bedtime.


#20

I haven’t used spacepen in a while, but it looks right.

Is everything defined when (@draft.get 0) runs? Does ReactDom.render expect jQuery?