11/** @jsx jsx */
22/** @jsxRuntime classic */
33import { jsx , css } from '@emotion/react' ;
4- import React , {
5- useEffect ,
6- useRef ,
7- FormEvent ,
8- KeyboardEvent ,
9- useState ,
10- } from 'react' ;
11- import { useRecoilState , useRecoilValue } from 'recoil' ;
4+ import React , { useEffect , useRef , KeyboardEvent , useState } from 'react' ;
5+ import { useRecoilValue } from 'recoil' ;
126
13- import {
14- blockMapState ,
15- blockRefState ,
16- draggingBlockState ,
17- throttleState ,
18- } from '@/stores' ;
7+ import { blockRefState , draggingBlockState , throttleState } from '@/stores' ;
198import { Block , BlockType } from '@/schemes' ;
209import {
2110 regex ,
@@ -25,7 +14,7 @@ import {
2514} from '@utils/blockContent' ;
2615import { useCommand , useManager } from '@/hooks' ;
2716import { focusState } from '@/stores/page' ;
28- import { moveBlock , updateBlock } from '@/utils' ;
17+ import { moveBlock , debounce } from '@/utils' ;
2918
3019const isGridOrColumn = ( block : Block ) : boolean =>
3120 block . type === BlockType . GRID || block . type === BlockType . COLUMN ;
@@ -72,14 +61,14 @@ const dragOverCss = () => css`
7261
7362function BlockContent ( blockDTO : Block ) {
7463 const contentEditableRef = useRef ( null ) ;
75- const [ blockMap , setBlockMap ] = useRecoilState ( blockMapState ) ;
7664 const focusId = useRecoilValue ( focusState ) ;
77- const caretRef = useRef ( 0 ) ;
7865 const listCnt = useRef ( 1 ) ;
7966 const [ Dispatcher ] = useCommand ( ) ;
80- const [ isBlur , setIsBlur ] = useState ( false ) ;
67+ const [
68+ { blockIndex, prevSiblings } ,
69+ { commitTransaction, startTransaction, setBlock, setCaretOffset } ,
70+ ] = useManager ( blockDTO . id ) ;
8171 const draggingBlock = useRecoilValue ( draggingBlockState ) ;
82- const [ { blockIndex } ] = useManager ( blockDTO . id ) ;
8372 const [ dragOverToggle , setDragOverToggle ] = useState ( false ) ;
8473
8574 useEffect ( ( ) => {
@@ -93,23 +82,7 @@ function BlockContent(blockDTO: Block) {
9382 if ( focusId === blockDTO . id ) contentEditableRef . current . focus ( ) ;
9483 } , [ focusId ] ) ;
9584
96- useEffect ( ( ) => {
97- const selection = window . getSelection ( ) ;
98- const nodeLength = selection . focusNode ?. nodeValue ?. length ?? 0 ;
99- if ( caretRef . current > nodeLength ) {
100- caretRef . current = nodeLength ;
101- }
102- selection . collapse ( selection . focusNode , caretRef . current ) ;
103- } , [ blockDTO . value ] ) ;
104-
105- const indexInSibling : number = blockMap [
106- blockDTO . parentId
107- ] . childIdList . findIndex ( ( childId : string ) => childId === blockDTO . id ) ;
108-
109- const upperBlocks : Array < Block > = blockMap [ blockDTO . parentId ] . childIdList
110- . map ( ( childId : any ) => blockMap [ childId ] )
111- . slice ( 0 , indexInSibling )
112- . reverse ( ) ;
85+ const upperBlocks : Array < Block > = prevSiblings . reverse ( ) ;
11386
11487 const isUpperBlockEqualToNumberList = ( ) : boolean => {
11588 if ( upperBlocks . length ) {
@@ -129,27 +102,27 @@ function BlockContent(blockDTO: Block) {
129102
130103 const FIRST_LIST_NUMBER = '1' ;
131104
132- const handleBlock = ( value : string , type ?: BlockType ) =>
133- type
134- ? setBlockMap ( {
135- ... blockMap ,
136- [ blockDTO . id ] : { ... blockDTO , value , type } ,
137- } )
138- : setBlockMap ( {
139- ... blockMap ,
140- [ blockDTO . id ] : { ... blockDTO , value } ,
141- } ) ;
105+ const handleBlock = ( value : string , type ?: BlockType , caretOffset = - 1 ) => {
106+ const { focusOffset } = window . getSelection ( ) ;
107+ startTransaction ( ) ;
108+ setBlock ( blockDTO . id , { value , type : type || blockDTO . type } ) ;
109+ contentEditableRef . current . blur ( ) ;
110+ setTimeout ( ( ) => {
111+ setCaretOffset ( caretOffset === - 1 ? focusOffset : caretOffset ) ;
112+ } ) ;
113+ commitTransaction ( ) ;
114+ } ;
142115
143- const handleValue = ( event : FormEvent < HTMLDivElement > ) => {
144- const content = event . currentTarget . textContent ;
116+ const handleValue = ( ) => {
117+ const content = contentEditableRef . current . textContent ;
145118 const newType = Object . entries ( regex ) . find ( ( testRegex ) =>
146119 testRegex [ 1 ] . test ( content ) ,
147120 ) ;
148121
149122 if ( newType ) {
150123 if ( newType [ 0 ] === BlockType . NUMBERED_LIST ) {
151- if ( ! indexInSibling && content [ 0 ] !== FIRST_LIST_NUMBER ) return ;
152- if ( indexInSibling ) {
124+ if ( ! blockIndex && content [ 0 ] !== FIRST_LIST_NUMBER ) return ;
125+ if ( blockIndex ) {
153126 const numberListUpperBlock = isUpperBlockEqualToNumberList ( ) ;
154127 if ( ! numberListUpperBlock && content [ 0 ] !== FIRST_LIST_NUMBER ) return ;
155128 if (
@@ -164,28 +137,11 @@ function BlockContent(blockDTO: Block) {
164137 content . slice ( content . indexOf ( ' ' ) + 1 , content . length ) ,
165138 newType [ 0 ] as BlockType ,
166139 ) ;
167- caretRef . current = 0 ;
168140 return ;
169141 }
170142 handleBlock ( content ) ;
171- const selection = window . getSelection ( ) ;
172- caretRef . current = selection . focusOffset ;
173- } ;
174-
175- const handleKeyUp = ( event : KeyboardEvent < HTMLDivElement > ) => {
176- const content = event . currentTarget . textContent ;
177- if (
178- event . key === 'Backspace' &&
179- ( ! blockDTO . value || ! window . getSelection ( ) . focusOffset )
180- ) {
181- handleBlock ( content , BlockType . TEXT ) ;
182- }
183-
184- if ( event . key === 'Enter' && event . shiftKey ) {
185- handleBlock ( content ) ;
186- caretRef . current = window . getSelection ( ) . focusOffset ;
187- }
188143 } ;
144+ const updateValue = useRef ( debounce ( handleValue , 300 ) ) . current ;
189145
190146 const handleKeyDown = ( event : KeyboardEvent < HTMLDivElement > ) => {
191147 const { focusNode, focusOffset } = window . getSelection ( ) ;
@@ -211,13 +167,6 @@ function BlockContent(blockDTO: Block) {
211167 }
212168 } ;
213169
214- useEffect ( ( ) => {
215- ( async ( ) => {
216- const { block : updatedBlock } = await updateBlock ( blockDTO ) ;
217- setBlockMap ( { ...blockMap , [ blockDTO . id ] : updatedBlock } ) ;
218- } ) ( ) ;
219- } , [ isBlur ] ) ;
220-
221170 useEffect ( ( ) => {
222171 blockRefState [ blockDTO . id ] = contentEditableRef ;
223172 return ( ) => {
@@ -226,32 +175,26 @@ function BlockContent(blockDTO: Block) {
226175 } , [ ] ) ;
227176
228177 useEffect ( ( ) => {
229- if ( focusId === blockDTO . id ) contentEditableRef . current . focus ( ) ;
178+ if ( focusId === blockDTO . id ) {
179+ contentEditableRef . current . focus ( ) ;
180+ }
230181 } , [ focusId ] ) ;
231182
232183 useEffect ( ( ) => {
233- const selection = window . getSelection ( ) ;
234- const nodeLength = selection . focusNode ?. nodeValue ?. length ?? 0 ;
235- if ( caretRef . current > nodeLength ) {
236- caretRef . current = nodeLength ;
237- }
238- selection . collapse ( selection . focusNode , caretRef . current ) ;
239-
240184 if ( blockDTO . type === BlockType . NUMBERED_LIST ) {
241185 const numberListUpperBlock = isUpperBlockEqualToNumberList ( ) ;
242- if ( ! indexInSibling || ! numberListUpperBlock ) {
186+ if ( ! blockIndex || ! numberListUpperBlock ) {
243187 listCnt . current = 1 ;
244188 return ;
245189 }
246190 if ( numberListUpperBlock ) {
247191 listCnt . current = cntOfUpperNumberListBlock ( ) + 1 ;
248192 }
249193 }
250- } , [ blockDTO . value ] ) ;
194+ } , [ blockDTO . type ] ) ;
251195
252196 const dragOverHandler = ( event : React . DragEvent < HTMLDivElement > ) => {
253197 event . dataTransfer . dropEffect = 'move' ;
254-
255198 event . preventDefault ( ) ;
256199 } ;
257200
@@ -269,14 +212,11 @@ function BlockContent(blockDTO: Block) {
269212 toId : blockDTO . parentId ,
270213 index : blockIndex + 1 ,
271214 } ) ;
272- setBlockMap ( ( prev ) => {
273- const next = { ...prev } ;
274- next [ block . id ] = block ;
275- fromBlock && ( next [ fromBlock . id ] = fromBlock ) ;
276- next [ to . id ] = to ;
277- return next ;
278- } ) ;
279-
215+ startTransaction ( ) ;
216+ setBlock ( block . id , block ) ;
217+ fromBlock && setBlock ( fromBlock . id , fromBlock ) ;
218+ setBlock ( to . id , to ) ;
219+ commitTransaction ( ) ;
280220 event . preventDefault ( ) ;
281221 } ;
282222
@@ -296,9 +236,7 @@ function BlockContent(blockDTO: Block) {
296236 onKeyDown = { handleKeyDown }
297237 suppressContentEditableWarning
298238 placeholder = { placeHolder [ blockDTO . type ] }
299- onInput = { handleValue }
300- onKeyUp = { handleKeyUp }
301- onBlur = { ( ) => setIsBlur ( ! isBlur ) }
239+ onInput = { updateValue }
302240 >
303241 { blockDTO . value }
304242 </ div >
0 commit comments