1- import { useEffect , useState } from "react" ;
1+ import { useState } from "react" ;
22import {
33 EditorRoot ,
44 EditorCommand ,
@@ -22,127 +22,83 @@ import { MathSelector } from "./selectors/math-selector";
2222import { TextButtons } from "./selectors/text-buttons" ;
2323import { ColorSelector } from "./selectors/color-selector" ;
2424
25- import { useDebouncedCallback } from "use-debounce" ;
26- import { useUpdatePage } from "@/hooks/usePages" ;
27-
2825const extensions = [ ...defaultExtensions , slashCommand ] ;
2926
27+ type EditorUpdateEvent = {
28+ editor : EditorInstance ;
29+ } ;
3030interface EditorProp {
3131 pageId : number ;
32- initialValue ?: JSONContent ;
33- onChange ?: ( value : JSONContent ) => void ;
32+ initialContent ?: JSONContent ;
33+ onEditorUpdate ?: ( event : EditorUpdateEvent ) => void ;
3434}
3535
36- // TODO: 나중에 title input 추가해야함
37- const Editor = ( { pageId, initialValue } : EditorProp ) => {
38- const [ initialContent , setInitialContent ] = useState < null | JSONContent > (
39- initialValue === undefined ? null : initialValue ,
40- ) ;
41-
42- const updatePageMutation = useUpdatePage ( pageId ) ;
43-
36+ const Editor = ( { initialContent, onEditorUpdate } : EditorProp ) => {
4437 const [ openNode , setOpenNode ] = useState ( false ) ;
4538 const [ openColor , setOpenColor ] = useState ( false ) ;
4639 const [ openLink , setOpenLink ] = useState ( false ) ;
47- const [ saveStatus , setSaveStatus ] = useState ( "Saved" ) ;
48-
49- const debouncedUpdates = useDebouncedCallback (
50- async ( editor : EditorInstance ) => {
51- if ( pageId === undefined ) return ;
52-
53- const json = editor . getJSON ( ) ;
54-
55- const response = await updatePageMutation . mutateAsync ( {
56- id : pageId ,
57- pageData : {
58- title : "제목 없음" ,
59- content : json ,
60- } ,
61- } ) ;
62- if ( response ) {
63- setSaveStatus ( "Saved" ) ;
64- }
65- } ,
66- 500 ,
67- ) ;
68-
69- useEffect ( ( ) => {
70- const content = window . localStorage . getItem ( pageId . toString ( ) ) ;
71- if ( content ) setInitialContent ( JSON . parse ( content ) ) ;
72- } , [ pageId ] ) ;
7340
7441 return (
75- < div className = "relative h-[720px] w-[520px] overflow-auto border-muted bg-background bg-white sm:rounded-lg sm:border sm:shadow-lg" >
76- < div className = "absolute right-5 top-5 z-10 mb-5 flex gap-2" >
77- < div className = "rounded-lg bg-accent px-2 py-1 text-sm text-muted-foreground" >
78- { saveStatus }
79- </ div >
80- </ div >
81- < EditorRoot >
82- < EditorContent
83- initialContent = { initialContent === null ? undefined : initialContent }
84- className = ""
85- extensions = { extensions }
86- editorProps = { {
87- handleDOMEvents : {
88- keydown : ( _view , event ) => handleCommandNavigation ( event ) ,
89- } ,
90- attributes : {
91- class : `prose prose-lg prose-headings:font-title font-default focus:outline-none max-w-full` ,
92- } ,
93- } }
94- slotAfter = { < ImageResizer /> }
95- onUpdate = { ( { editor } ) => {
96- debouncedUpdates ( editor ) ;
97- setSaveStatus ( "Unsaved" ) ;
42+ < EditorRoot >
43+ < EditorContent
44+ initialContent = { initialContent }
45+ className = ""
46+ extensions = { extensions }
47+ editorProps = { {
48+ handleDOMEvents : {
49+ keydown : ( _view , event ) => handleCommandNavigation ( event ) ,
50+ } ,
51+ attributes : {
52+ class : `prose prose-lg prose-headings:font-title font-default focus:outline-none max-w-full` ,
53+ } ,
54+ } }
55+ slotAfter = { < ImageResizer /> }
56+ onUpdate = { onEditorUpdate }
57+ >
58+ < EditorCommand className = "z-50 h-auto max-h-[330px] overflow-y-auto rounded-md border border-muted bg-background px-1 py-2 shadow-md transition-all" >
59+ < EditorCommandEmpty className = "px-2 text-muted-foreground" >
60+ No results
61+ </ EditorCommandEmpty >
62+ < EditorCommandList >
63+ { suggestionItems . map ( ( item ) => (
64+ < EditorCommandItem
65+ value = { item . title }
66+ onCommand = { ( val ) => item . command ?.( val ) }
67+ className = "flex w-full items-center space-x-2 rounded-md px-2 py-1 text-left text-sm hover:cursor-pointer hover:bg-accent aria-selected:bg-accent"
68+ key = { item . title }
69+ >
70+ < div className = "flex h-10 w-10 items-center justify-center rounded-md border border-muted bg-background" >
71+ { item . icon }
72+ </ div >
73+ < div >
74+ < p className = "font-medium" > { item . title } </ p >
75+ < p className = "text-xs text-muted-foreground" >
76+ { item . description }
77+ </ p >
78+ </ div >
79+ </ EditorCommandItem >
80+ ) ) }
81+ </ EditorCommandList >
82+ </ EditorCommand >
83+ < EditorBubble
84+ tippyOptions = { {
85+ placement : "top" ,
9886 } }
87+ className = "flex w-fit max-w-[90vw] overflow-hidden rounded-md border border-muted bg-background shadow-xl"
9988 >
100- < EditorCommand className = "z-50 h-auto max-h-[330px] overflow-y-auto rounded-md border border-muted bg-background px-1 py-2 shadow-md transition-all" >
101- < EditorCommandEmpty className = "px-2 text-muted-foreground" >
102- No results
103- </ EditorCommandEmpty >
104- < EditorCommandList >
105- { suggestionItems . map ( ( item ) => (
106- < EditorCommandItem
107- value = { item . title }
108- onCommand = { ( val ) => item . command ?.( val ) }
109- className = "flex w-full items-center space-x-2 rounded-md px-2 py-1 text-left text-sm hover:cursor-pointer hover:bg-accent aria-selected:bg-accent"
110- key = { item . title }
111- >
112- < div className = "flex h-10 w-10 items-center justify-center rounded-md border border-muted bg-background" >
113- { item . icon }
114- </ div >
115- < div >
116- < p className = "font-medium" > { item . title } </ p >
117- < p className = "text-xs text-muted-foreground" >
118- { item . description }
119- </ p >
120- </ div >
121- </ EditorCommandItem >
122- ) ) }
123- </ EditorCommandList >
124- </ EditorCommand >
125- < EditorBubble
126- tippyOptions = { {
127- placement : "top" ,
128- } }
129- className = "flex w-fit max-w-[90vw] overflow-hidden rounded-md border border-muted bg-background shadow-xl"
130- >
131- { " " }
132- < Separator orientation = "vertical" />
133- < NodeSelector open = { openNode } onOpenChange = { setOpenNode } />
134- < Separator orientation = "vertical" />
135- < LinkSelector open = { openLink } onOpenChange = { setOpenLink } />
136- < Separator orientation = "vertical" />
137- < MathSelector />
138- < Separator orientation = "vertical" />
139- < TextButtons />
140- < Separator orientation = "vertical" />
141- < ColorSelector open = { openColor } onOpenChange = { setOpenColor } />
142- </ EditorBubble >
143- </ EditorContent >
144- </ EditorRoot >
145- </ div >
89+ < Separator orientation = "vertical" />
90+ < NodeSelector open = { openNode } onOpenChange = { setOpenNode } />
91+ < Separator orientation = "vertical" />
92+ < LinkSelector open = { openLink } onOpenChange = { setOpenLink } />
93+ < Separator orientation = "vertical" />
94+ < MathSelector />
95+ < Separator orientation = "vertical" />
96+ < TextButtons />
97+ < Separator orientation = "vertical" />
98+ < ColorSelector open = { openColor } onOpenChange = { setOpenColor } />
99+ </ EditorBubble >
100+ </ EditorContent >
101+ </ EditorRoot >
146102 ) ;
147103} ;
148104
0 commit comments