Slide 1

Slide 1 text

Text editing & immutable data structures What has Draft.js eaten Vesa Vänskä, Kisko Labs Vesa Vänskä 2017 @vesan

Slide 2

Slide 2 text

Draft.js? Vesa Vänskä 2017 @vesan

Slide 3

Slide 3 text

Rich text editor framework for React Vesa Vänskä 2017 @vesan

Slide 4

Slide 4 text

Demo Vesa Vänskä 2017 @vesan

Slide 5

Slide 5 text

Code example import {Editor, EditorState} from 'draft-js'; class BasicEditor extends React.Component { constructor(props) { super(props); this.state = {editorState: EditorState.createEmpty()}; this.onChange = (editorState) => this.setState({editorState}); } render() { return ; } } Vesa Vänskä 2017 @vesan

Slide 6

Slide 6 text

Why I want to talk about it? Vesa Vänskä 2017 @vesan

Slide 7

Slide 7 text

Vesa Vänskä → Software Developer & Partner at @kiskolabs → Ruby on the backend, JavaScript on the frontend → React.js as the frontend tool of choice for two-ish years Vesa Vänskä 2017 @vesan

Slide 8

Slide 8 text

Draft.js State Structure Vesa Vänskä 2017 @vesan

Slide 9

Slide 9 text

Draft.js state structure Immutable.js Vesa Vänskä 2017 @vesan

Slide 10

Slide 10 text

Draft.js state structure Immutable.js Immutable.js provides many Persistent Immutable data structures including: List, Stack, Map, OrderedMap, Set, OrderedSet and Record. Vesa Vänskä 2017 @vesan

Slide 11

Slide 11 text

// Immutable.js Map const map1 = Map({ a: 1, b: 2, c: 3 }) const map2 = map1.set('b', 50) map1.get('b') // 2 map2.get('b') // 50 // Regular Object const map1 = { a: 1, b: 2, c: 3 } const map2 = map1 map2['b'] = 50 map1['b'] // 50 map2['b'] // 50 Vesa Vänskä 2017 @vesan

Slide 12

Slide 12 text

Draft.js State Structure EditorState → The current text content state → The current selection state → The fully decorated representation of the contents → Undo/redo stacks Vesa Vänskä 2017 @vesan

Slide 13

Slide 13 text

Draft.js State Structure EditorState state tree → Editor → Content → Selection → Current inline style Vesa Vänskä 2017 @vesan

Slide 14

Slide 14 text

Draft.js State Structure ContentState → The entire contents of an editor: text, block and inline styles, and entity ranges. → Two selection states of an editor: before and after the rendering of these contents. Vesa Vänskä 2017 @vesan

Slide 15

Slide 15 text

Draft.js State Structure Block Map → Ordered map that has ContentBlocks → Single blocks of content → Information about type of content, attached entities, inline styles and depth Vesa Vänskä 2017 @vesan

Slide 16

Slide 16 text

Draft.js State Structure SelectionState Represents a selection range in the editor. Selection points: anchor & focus values which both have ContentBlock key and offset. Vesa Vänskä 2017 @vesan

Slide 17

Slide 17 text

SelectionState → anchorKey → anchorOffset → focusKey → focusOffset → isBackward → hasFocus Vesa Vänskä 2017 @vesan

Slide 18

Slide 18 text

Entities Used to annotate ranges of text with metadata. Vesa Vänskä 2017 @vesan

Slide 19

Slide 19 text

That's enough of state structure for today Vesa Vänskä 2017 @vesan

Slide 20

Slide 20 text

How to change the state (low-level) editorState.push( editorState, contentState, changeType ) Vesa Vänskä 2017 @vesan

Slide 21

Slide 21 text

How to change the state (high-level) Modifier → replaceText → insertText → moveText → applyInlineStyle → removeInlineStyle Vesa Vänskä 2017 @vesan

Slide 22

Slide 22 text

How to change the state (high-level) Modifier All return new ContentState. Vesa Vänskä 2017 @vesan

Slide 23

Slide 23 text

How to get something to the screen Vesa Vänskä 2017 @vesan

Slide 24

Slide 24 text

Converting EditorState to DOM Vesa Vänskä 2017 @vesan

Slide 25

Slide 25 text

Extending the rendering Vesa Vänskä 2017 @vesan

Slide 26

Slide 26 text

Extending the rendering For visual changes, you can change the block rendering Vesa Vänskä 2017 @vesan

Slide 27

Slide 27 text

Extending the rendering Custom block map How to map content types to components. Vesa Vänskä 2017 @vesan

Slide 28

Slide 28 text

Extending the rendering Custom block map example const blockRenderMap = Immutable.Map({ 'SectionWrapper': { element: 'section', wrapper: } }); Vesa Vänskä 2017 @vesan

Slide 29

Slide 29 text

Extending the rendering Custom block renderer Good for totally custom components. Vesa Vänskä 2017 @vesan

Slide 30

Slide 30 text

Extending the rendering Custom block renderer example function mediaBlockRenderer(contentBlock) { const type = contentBlock.getType(); if (type === 'atomic') { return { component: MediaComponent, editable: false, props: { foo: 'bar', }, }; } } Vesa Vänskä 2017 @vesan

Slide 31

Slide 31 text

Extending the rendering For deeper changes, you need an editor decorator Vesa Vänskä 2017 @vesan

Slide 32

Slide 32 text

Extending the rendering Editor decorator const decorator = new CompositeDecorator([{ strategy: numberedHeadingsStrategy, component: NumberedHeading, }]); EditorState.createEmpty(decorator) Vesa Vänskä 2017 @vesan

Slide 33

Slide 33 text

Extending the rendering Editor decorator strategy const numberedHeadingsStrategy = (contentBlock, callback, contentState) => { const type = contentBlock.getType(); const isHeading = ["header-one", "header-two", "header-three", "header-four", "header-five"].includes(type); if (isHeading) { callback(0, contentBlock.getText().length); } } Vesa Vänskä 2017 @vesan

Slide 34

Slide 34 text

Extending the rendering Editor decorator custom component const NumberedHeading = (props) => { let key = props.offsetKey.match(/^([^-]+)-/)[1]; const data = props.contentState.getBlockForKey(key).getData() const number = data && data.get("number"); return ( {props.children} ); }; Vesa Vänskä 2017 @vesan

Slide 35

Slide 35 text

Demo Adding numbered headings Vesa Vänskä 2017 @vesan

Slide 36

Slide 36 text

Thanks! @vesan vesa@vesavanska.com Vesa Vänskä 2017 @vesan