11// @flow
22
33import React , { Component } from 'react'
4- import { withPluginContext } from '@djsp/core'
4+ import { withPluginContext , constants } from '@djsp/core'
55import type { PluginProps } from '@djsp/core'
6- import Draft from 'draft-js'
6+ import { ContentBlock , EditorState , Modifier , SelectionState } from 'draft-js'
7+ import { insertNewLine } from '@djsp/utils'
8+ import DraftOffsetKey from 'draft-js/lib/DraftOffsetKey'
79import AtomicBlock from './AtomicBlock'
810
9- const { EditorState } = Draft
10-
1111type Props = PluginProps & {
1212 type : string ,
1313 children : any ,
@@ -17,6 +17,38 @@ type State = {
1717 isFocused : boolean ,
1818}
1919
20+ // Set selection of editor to next/previous block
21+ const setSelection = (
22+ editorState : EditorState ,
23+ setEditorState : EditorState ,
24+ newActiveBlock : ContentBlock
25+ ) : void => {
26+ // TODO verify that always a key-0-0 exists
27+ const offsetKey = DraftOffsetKey . encode ( newActiveBlock . getKey ( ) , 0 , 0 )
28+ const node = document . querySelectorAll ( `[data-offset-key="${ offsetKey } "]` ) [ 0 ]
29+ // set the native selection to the node so the caret is not in the text and
30+ // the selectionState matches the native selection
31+ const selection = window . getSelection ( )
32+ const range = document . createRange ( )
33+ range . setStart ( node , 0 )
34+ range . setEnd ( node , 0 )
35+ selection . removeAllRanges ( )
36+ selection . addRange ( range )
37+
38+ setEditorState (
39+ EditorState . forceSelection (
40+ editorState ,
41+ new SelectionState ( {
42+ anchorKey : newActiveBlock . getKey ( ) ,
43+ anchorOffset : 0 ,
44+ focusKey : newActiveBlock . getKey ( ) ,
45+ focusOffset : 0 ,
46+ isBackward : false ,
47+ } )
48+ )
49+ )
50+ }
51+
2052class AtomicBlockPlugin extends Component < Props , State > {
2153 unregister : ( ) => void
2254
@@ -26,38 +58,90 @@ class AtomicBlockPlugin extends Component<Props, State> {
2658 const { registerPlugin } = this . props
2759
2860 this . unregister = registerPlugin ( {
29- keyBindingFn : this . keyBindingFn ,
3061 blockRendererFn : this . blockRendererFn ,
62+ handleReturn : this . handleReturn ,
63+ handleKeyCommand : this . handleKeyCommand ,
3164 } )
3265 }
3366
3467 componentWillUnmount ( ) {
3568 this . unregister ( )
3669 }
3770
38- focusBlock = ( blockKey : string ) => {
39- const { setEditorState, editorState } = this . props
71+ handleKeyCommand = ( command , editorState ) => {
72+ const { setEditorState } = this . props
4073
41- let selection = editorState . getSelection ( )
74+ let contentState = editorState . getCurrentContent ( )
75+ const selection = editorState . getSelection ( )
76+ const key = selection . getStartKey ( )
77+ const currentBlock = contentState . getBlockForKey ( key )
78+ const previousBlock = contentState . getBlockBefore ( key )
79+
80+ if ( ! selection . isCollapsed ( ) ) {
81+ return constants . NOT_HANDLED
82+ } else if (
83+ currentBlock . getType ( ) !== 'atomic' &&
84+ previousBlock != null &&
85+ selection . getStartOffset ( ) === 0 &&
86+ previousBlock . getType ( ) === 'atomic' &&
87+ command === 'backspace'
88+ ) {
89+ setSelection ( editorState , setEditorState , previousBlock )
90+ return constants . HANDLED
91+ } else if (
92+ currentBlock . getType ( ) === 'atomic' &&
93+ [ 'backspace' , 'delete' ] . includes ( command )
94+ ) {
95+ contentState = Modifier . removeRange (
96+ contentState ,
97+ editorState . getSelection ( ) . merge ( {
98+ anchorOffset : 0 ,
99+ focusOffset : 1 ,
100+ } ) ,
101+ null
102+ )
103+
104+ setEditorState (
105+ EditorState . push (
106+ editorState ,
107+ Modifier . setBlockType ( contentState , selection , 'unstyled' )
108+ )
109+ )
110+ return constants . HANDLED
111+ }
42112
43- selection = selection . merge ( {
44- anchorKey : blockKey ,
45- anchorOffset : 0 ,
46- focusKey : blockKey ,
47- focusOffset : 0 ,
48- } )
113+ return constants . NOT_HANDLED
114+ }
49115
50- window . getSelection ( ) . removeAllRanges ( )
116+ focusBlock = ( blockKey : string ) => {
117+ const { setEditorState, editorState } = this . props
118+ const block = editorState . getCurrentContent ( ) . getBlockForKey ( blockKey )
51119
52- setEditorState ( EditorState . forceSelection ( editorState , selection ) )
120+ setSelection ( editorState , setEditorState , block )
53121 }
54122
55- keyBindingFn = ( event : SyntheticKeyboardEvent < * > ) => {
56- console. log ( 'event.key' , event . key )
123+ deleteAtomicBlock = ( key : string ) => {
124+ const { editorState, setEditorState } = this . props
125+ const selection = editorState . getSelection ( )
126+
127+ setEditorState (
128+ EditorState . push (
129+ editorState ,
130+ Modifier . removeRange (
131+ editorState . getCurrentContent ( ) ,
132+ selection . merge ( {
133+ anchorKey : key ,
134+ focusKey : key ,
135+ anchorOffset : 0 ,
136+ focusOffset : 1 ,
137+ } )
138+ )
139+ )
140+ )
57141 }
58142
59143 renderChildren = ( props : Object ) => {
60- const { editorState } = this . props
144+ const { editorState, setEditorState } = this . props
61145
62146 const blockKey = props . block . getKey ( )
63147 const selection = editorState . getSelection ( )
@@ -66,13 +150,21 @@ class AtomicBlockPlugin extends Component<Props, State> {
66150
67151 return (
68152 < AtomicBlock
153+ onDeleteBlock = { ( ) => this . deleteAtomicBlock ( blockKey ) }
154+ setEditorState = { setEditorState }
69155 isFocused = { isFocused }
70156 onClick = { ( ) => this . focusBlock ( blockKey ) } >
71- { this . props . children ( props ) }
157+ { this . props . children ( { ... props , isFocused } ) }
72158 </ AtomicBlock >
73159 )
74160 }
75161
162+ handleReturn = ( event , editorState ) => {
163+ const { setEditorState } = this . props
164+
165+ setEditorState ( insertNewLine ( editorState ) )
166+ }
167+
76168 blockRendererFn = block => {
77169 const { type, editorState } = this . props
78170 const content = editorState . getCurrentContent ( )
0 commit comments