Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Edição de Texto Rico com React e Draft.js

585 views

Published on

Edição de Texto Rico com React e Draft.js

Published in: Software
  • Be the first to comment

  • Be the first to like this

Edição de Texto Rico com React e Draft.js

  1. 1. EdiçãodeTextoRico comReacteDraft.js Guilherme Vierno
  2. 2. Aboutme • Mineiro • Físicoporformação • Fullstackdev-8anos • Desenvolvedorna • Plataformasdepublicação(admin) • megadraft.io
  3. 3. Editoresdetextorico naWeb
  4. 4. Editoresdetextorico Servempara: • Mudaraaparênciadotexto(negrito,itálico,etc) • Adicionarmídias,comoimagensevídeos • Criarrelaçõesentreconteúdosatravésdelinks • … SemeditarHTMLnamão…
  5. 5. OldSchool CKEditor(CKEditor.com) Fonte: https://www.turnkeylinux.org/blog/tinymce-vs-ckeditor
  6. 6. OldSchool TinyMCE(https://www.tinymce.com) Fonte: https://www.turnkeylinux.org/blog/tinymce-vs-ckeditor
  7. 7. • HTML/DOM===State • Copiar&Colar • Desfazer/Refazer • Crossbrowser&ContentEditable Desafios
  8. 8. NewSchool
  9. 9. NewSchool
  10. 10. NewSchool
  11. 11. React& Draft.js
  12. 12. Draft.js ODraft.jséumframeworkconstruídopeloFacebook usandoReact,queusaummodeloimutávelefacilitaa construçãodeeditoresdetextorico,abstraindoas diferençasentreosnavegadores • OpenSource • Seminterface,éumframework,nãoumeditor • Tudoécustomizável • Controleestritodoconteúdo(ContentEditable+React) • Controleestritodocursor(SelectionAPI)
  13. 13. Draft.js Oestadodoeditorécompostopordoissub-estados imutáveis: • EditorState • ContentState • SelectionState
  14. 14. Draft.js import React from 'react'; import ReactDOM from 'react-dom'; import {Editor, EditorState} from 'draft-js'; class MyEditor extends React.Component { constructor(props) { super(props); this.state = {editorState: EditorState.createEmpty()}; this.onChange = (editorState) => this.setState({editorState}); } render() { return ( <Editor editorState={this.state.editorState} onChange={this.onChange} /> ); } } ReactDOM.render( <MyEditor />, document.getElementById('container') );
  15. 15. Draft.js import React from 'react'; import ReactDOM from 'react-dom'; import {Editor, EditorState} from 'draft-js'; class MyEditor extends React.Component { constructor(props) { super(props); this.state = {editorState: EditorState.createEmpty()}; this.onChange = (editorState) => this.setState({editorState}); } render() { return ( <Editor editorState={this.state.editorState} onChange={this.onChange} /> ); } } ReactDOM.render( <MyEditor />, document.getElementById('container') );
  16. 16. Draft.js Conteúdocontroladoatravésdoestadoimutável Map { content: List [ Map { "text": "lorem ipsum" } ], selection: Map { start: 6, end: 6, block: 0 } } ENTER
  17. 17. Draft.js Égeradoumnovoestadoatravésdemodificadores Map { content: List [ Map { "text": “lorem ipsum" }, Map { "text": "ipsum" } ], selection: Map { start: 0, end: 0, block: 1 } } SplitBlock SetSelection
  18. 18. Draft.js Undo/Redo Map { content: List [ Map { "text": “lorem" }, Map { "text": "ipsum" } ], selection: Map { start: 0, end: 0, block: 1 } } Map { content: List [ { "text": "lorem ipsum" } ], selection: Map { start: 6, end: 6, block: 0 } }
  19. 19. Estadoserializável ConvertendooestadoparaJSON const content = convertFromRaw(rawState) const EditorState = EditorState.createWithContent(state) const rawState = convertToRaw(contentState) CriandooestadoapartirdeumJSON
  20. 20. { "entityMap": {}, "blocks": [ { "key": "ag6qs", "text": "", "type": "unstyled", "depth": 0, "inlineStyleRanges": [], "entityRanges": [], "data": {} } ] } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ContentState
  21. 21. { "entityMap": {}, "blocks": [ { "key": "ag6qs", "text": "megadraft", "type": "unstyled", "depth": 0, "inlineStyleRanges": [], "entityRanges": [], "data": {} }, { "key": "dg2bk", "text": "rock'n'roll", "type": "unstyled", "depth": 0, "inlineStyleRanges": [], "entityRanges": [], "data": {} } ] } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 Blocks
  22. 22. { "entityMap": {}, "blocks": [ { "key": "ag6qs", "text": "rock n roll", "type": "unstyled", "depth": 0, "inlineStyleRanges": [ { "offset": 7, "length": 4, "style": "BOLD" } ], "entityRanges": [], "data": {} } ] } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 inlineStyleRanges
  23. 23. { "entityMap": { "0": { "type": "LINK", "mutability": "MUTABLE", "data": { "url": "https://megadraft.io/LICENSE" } } }, "blocks": [ { "key": "ag6qs", "text": "Megadraft is MIT licensed", "type": "unstyled", "depth": 0, "inlineStyleRanges": [], "entityRanges": [ { "offset": 13, "length": 3, "key": 0 } ], "data": {} } ] } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 Entity
  24. 24. { "entityMap": {}, "blocks": [ { "key": "ag6qs", "text": "Good usability", "type": "ordered-list-item", "depth": 0, "inlineStyleRanges": [], "entityRanges": [], "data": {} }, { "key": "1pdm1", "text": "A nice default base of plugins", "type": "ordered-list-item", "depth": 1, "inlineStyleRanges": [], "entityRanges": [], "data": {} } ] } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 depth
  25. 25. { "entityMap": {}, "blocks": [ { "key": "ag6qs", "text": "", "type": "image", "depth": 0, "inlineStyleRanges": [], "entityRanges": [], "data": { "url": "http://example.com/image.jpg", "credits": "My credits", "legend": "Legendary", "height": 1920, "width": 1080, "left": 0, "top": 0, "type": "image/jpeg", } } ] } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 data
  26. 26. • blockRenderMap(mapeiatiposdeblocosparatagsHTML) • blockRenderFn(renderizaumblockqualquer) • blockStyleFn(classNamedosblocks) • decorators(envolvetextoemumcomponente) • customStyleMap(defineoestilodeumestiloinline) Customização
  27. 27. ExportaroconteúdodoDraft.js Existemalgunsprojetosqueconvertemomodelo: • https://github.com/sstur/draft-js-export-html • https://github.com/rkpasia/draft-js-exporter • https://github.com/globocom/react-native-draftjs-render • …
  28. 28. Megadraft
  29. 29. Megadraft OMegadraftéumeditordetextoricoconstruídocomos frameworksDraft.jseReact. • Boausabilidade • Umabasepadrãoagradáveldeplugins • Extensibilidade • OpenSource
  30. 30. import {MegadraftEditor, editorStateFromRaw} from "megadraft"; class App extends React.Component { constructor(props) { super(props); this.state = {editorState: editorStateFromRaw(null)}; this.onChange = ::this.onChange; } onChange(editorState) { this.setState({editorState}); } render() { return ( <MegadraftEditor editorState={this.state.editorState} onChange={this.onChange} /> ) } } ReactDOM.render( <App />, document.getElementById("container") ); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 ComousaroMegadraft
  31. 31. ComousaroMegadraft <link href="node_modules/megadraft/dist/css/megadraft.css" rel="stylesheet">
  32. 32. class Example extends React.Component { ... render() { return ( <MegadraftEditor plugins={[MyPlugin1, MyPlugin2]} ... /> ); } } 1 2 3 4 5 6 7 8 Plugins
  33. 33. PluginGenerator https://github.com/globocom/generator-megadraft-plugin npm install -g yo npm install -g generator-megadraft-plugin yo megadraft-plugin
  34. 34. import ImageButton from "./ImageButton"; import ImageBlock from "./ImageBlock"; export default { // Friendly plugin name title: "Image", // A unique plugin name used to identify the plugin and its blocks type: "image", // React component to be rendered in the block sidebar buttonComponent: ImageButton, // React component for rendering the content block blockComponent: ImageBlock }; 1 2 3 4 5 6 7 8 9 10 11 12 13 PluginStructure
  35. 35. import React, {Component} from "react"; import {DraftJS, insertDataBlock} from "megadraft"; export default class BlockButton extends Component { constructor(props) { this.onClick = ::this.onClick; } onClick(e) { e.preventDefault(); const src = window.prompt("Enter a URL"); const data = {"type": "image", "src": src}; // Calls the onChange method with the new state. this.props.onChange(insertDataBlock(this.props.editorState, data)); } render() { return ( <button className={this.props.className} onClick={this.onClick}> <SomeIcon className="sidemenu__button__icon" /> </button> ); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 buttonComponent
  36. 36. import React, {Component} from "react"; export default class ImageBlock extends Component { render(){ return ( <img src={this.props.data.src} /> ); } } 1 2 3 4 5 6 7 8 9 blockComponent
  37. 37. Slate
  38. 38. queosdeusesdoheavymetalestejamcomvocês Obrigado! GuilhermeVierno vierno.com.br megadraft.io

×