@@ -24,6 +24,8 @@ import { ColorSelector } from "./selectors/color-selector";
2424
2525import { useDebouncedCallback } from "use-debounce" ;
2626
27+ import { useUpdatePage } from "@/hooks/usePages" ;
28+
2729const extensions = [ ...defaultExtensions , slashCommand ] ;
2830
2931interface EditorProp {
@@ -40,31 +42,22 @@ const Editor = ({ pageId, initialValue }: EditorProp) => {
4042 const [ openNode , setOpenNode ] = useState ( false ) ;
4143 const [ openColor , setOpenColor ] = useState ( false ) ;
4244 const [ openLink , setOpenLink ] = useState ( false ) ;
45+ const [ saveStatus , setSaveStatus ] = useState ( "Saved" ) ;
4346
44- const highlightCodeblocks = ( content : string ) => {
45- const doc = new DOMParser ( ) . parseFromString ( content , "text/html" ) ;
46- doc . querySelectorAll ( "pre code" ) . forEach ( ( el ) => {
47- // @ts -expect-error - highlightElement is not in the types
48- // https://highlightjs.readthedocs.io/en/latest/api.html?highlight=highlightElement#highlightelement
49- hljs . highlightElement ( el ) ;
50- } ) ;
51- return new XMLSerializer ( ) . serializeToString ( doc ) ;
52- } ;
47+ const updatePageMutation = useUpdatePage ( ) ;
5348
5449 const debouncedUpdates = useDebouncedCallback (
5550 async ( editor : EditorInstance ) => {
5651 if ( pageId === undefined ) return ;
5752
5853 const json = editor . getJSON ( ) ;
59- window . localStorage . setItem (
60- "html-content" ,
61- highlightCodeblocks ( editor . getHTML ( ) ) ,
62- ) ;
63- window . localStorage . setItem ( pageId . toString ( ) , JSON . stringify ( json ) ) ;
64- window . localStorage . setItem (
65- "markdown" ,
66- editor . storage . markdown . getMarkdown ( ) ,
67- ) ;
54+ const response = await updatePageMutation . mutateAsync ( {
55+ id : pageId ,
56+ pageData : json ,
57+ } ) ;
58+ if ( response ) {
59+ setSaveStatus ( "Saved" ) ;
60+ }
6861 } ,
6962
7063 500 ,
@@ -75,72 +68,78 @@ const Editor = ({ pageId, initialValue }: EditorProp) => {
7568 if ( content ) setInitialContent ( JSON . parse ( content ) ) ;
7669 } , [ pageId ] ) ;
7770
78- console . log ( initialContent ) ;
79-
8071 return (
81- < EditorRoot >
82- < EditorContent
83- initialContent = { initialContent === null ? undefined : initialContent }
84- className = "relative h-[720px] w-[520px] overflow-auto border-muted bg-background bg-white sm:rounded-lg sm:border sm:shadow-lg"
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- } }
98- >
99- < 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" >
100- < EditorCommandEmpty className = "px-2 text-muted-foreground" >
101- No results
102- </ EditorCommandEmpty >
103- < EditorCommandList >
104- { suggestionItems . map ( ( item ) => (
105- < EditorCommandItem
106- value = { item . title }
107- onCommand = { ( val ) => item . command ?.( val ) }
108- 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"
109- key = { item . title }
110- >
111- < div className = "flex h-10 w-10 items-center justify-center rounded-md border border-muted bg-background" >
112- { item . icon }
113- </ div >
114- < div >
115- < p className = "font-medium" > { item . title } </ p >
116- < p className = "text-xs text-muted-foreground" >
117- { item . description }
118- </ p >
119- </ div >
120- </ EditorCommandItem >
121- ) ) }
122- </ EditorCommandList >
123- </ EditorCommand >
124- < EditorBubble
125- tippyOptions = { {
126- placement : "top" ,
72+ < div className = "relative h-[720px] w-[520px] overflow-auto border-muted bg-background bg-white sm:rounded-lg sm:border sm:shadow-lg" >
73+ < div className = "absolute right-5 top-5 z-10 mb-5 flex gap-2" >
74+ < div className = "rounded-lg bg-accent px-2 py-1 text-sm text-muted-foreground" >
75+ { saveStatus }
76+ </ div >
77+ </ div >
78+ < EditorRoot >
79+ < EditorContent
80+ initialContent = { initialContent === null ? undefined : initialContent }
81+ className = ""
82+ extensions = { extensions }
83+ editorProps = { {
84+ handleDOMEvents : {
85+ keydown : ( _view , event ) => handleCommandNavigation ( event ) ,
86+ } ,
87+ attributes : {
88+ class : `prose prose-lg prose-headings:font-title font-default focus:outline-none max-w-full` ,
89+ } ,
90+ } }
91+ slotAfter = { < ImageResizer /> }
92+ onUpdate = { ( { editor } ) => {
93+ debouncedUpdates ( editor ) ;
94+ setSaveStatus ( "Unsaved" ) ;
12795 } }
128- className = "flex w-fit max-w-[90vw] overflow-hidden rounded-md border border-muted bg-background shadow-xl"
12996 >
130- { " " }
131- < Separator orientation = "vertical" />
132- < NodeSelector open = { openNode } onOpenChange = { setOpenNode } />
133- < Separator orientation = "vertical" />
134- < LinkSelector open = { openLink } onOpenChange = { setOpenLink } />
135- < Separator orientation = "vertical" />
136- < MathSelector />
137- < Separator orientation = "vertical" />
138- < TextButtons />
139- < Separator orientation = "vertical" />
140- < ColorSelector open = { openColor } onOpenChange = { setOpenColor } />
141- </ EditorBubble >
142- </ EditorContent >
143- </ EditorRoot >
97+ < 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" >
98+ < EditorCommandEmpty className = "px-2 text-muted-foreground" >
99+ No results
100+ </ EditorCommandEmpty >
101+ < EditorCommandList >
102+ { suggestionItems . map ( ( item ) => (
103+ < EditorCommandItem
104+ value = { item . title }
105+ onCommand = { ( val ) => item . command ?.( val ) }
106+ 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"
107+ key = { item . title }
108+ >
109+ < div className = "flex h-10 w-10 items-center justify-center rounded-md border border-muted bg-background" >
110+ { item . icon }
111+ </ div >
112+ < div >
113+ < p className = "font-medium" > { item . title } </ p >
114+ < p className = "text-xs text-muted-foreground" >
115+ { item . description }
116+ </ p >
117+ </ div >
118+ </ EditorCommandItem >
119+ ) ) }
120+ </ EditorCommandList >
121+ </ EditorCommand >
122+ < EditorBubble
123+ tippyOptions = { {
124+ placement : "top" ,
125+ } }
126+ className = "flex w-fit max-w-[90vw] overflow-hidden rounded-md border border-muted bg-background shadow-xl"
127+ >
128+ { " " }
129+ < Separator orientation = "vertical" />
130+ < NodeSelector open = { openNode } onOpenChange = { setOpenNode } />
131+ < Separator orientation = "vertical" />
132+ < LinkSelector open = { openLink } onOpenChange = { setOpenLink } />
133+ < Separator orientation = "vertical" />
134+ < MathSelector />
135+ < Separator orientation = "vertical" />
136+ < TextButtons />
137+ < Separator orientation = "vertical" />
138+ < ColorSelector open = { openColor } onOpenChange = { setOpenColor } />
139+ </ EditorBubble >
140+ </ EditorContent >
141+ </ EditorRoot >
142+ </ div >
144143 ) ;
145144} ;
146145
0 commit comments