Skip to content

Commit

Permalink
feat: complete md_editor
Browse files Browse the repository at this point in the history
  • Loading branch information
yuanyxh committed May 7, 2024
1 parent 041e6a7 commit 9ea79e4
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 57 deletions.
10 changes: 5 additions & 5 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

58 changes: 49 additions & 9 deletions src/filehandle/md_editor/component/MDContent.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { useEffect, useMemo, useState } from 'react';
import { useEffect, useMemo, useRef, useState } from 'react';

import { App } from 'antd';

import { type DH, type FH, writeFile } from '@/filehandle/utils/fileManager';
import type { DH, FH } from '@/filehandle/utils/fileManager';
import { writeFile } from '@/filehandle/utils/fileManager';

import type { IMDEditorExpose } from './MDEditor';
import MDEditor from './MDEditor';
import { Sidebar } from './MDSidebar';
import styles from './styles/MDContent.module.less';
Expand All @@ -19,11 +21,14 @@ interface IMDContentProps {
export const MDContent: React.FC<Readonly<IMDContentProps>> = (props) => {
const { handle } = props;

const { message } = App.useApp();
const { message, modal } = App.useApp();

const [markdown, setMarkdown] = useState('');
const [changed, setChanged] = useState(false);
const [currentHandle, setCurrentHandle] = useState<FH | null>(null);

const editorRef = useRef<IMDEditorExpose>(null);

useMemo(() => {
currentHandle &&
getMarkdown(currentHandle).then((text) => {
Expand All @@ -38,25 +43,60 @@ export const MDContent: React.FC<Readonly<IMDContentProps>> = (props) => {
}, []);

const handleSelect = (handle: FH) => {
setCurrentHandle(handle);
if (!changed) return setCurrentHandle(handle);

modal.confirm({
title: '温馨提示',
content: '是否保存更改?如果不保存,您的更改会丢失。',
okText: '保存',
cancelText: '放弃更改',
onOk() {
if (currentHandle && editorRef.current) {
writeFile(currentHandle, editorRef.current.getMarkdown())
.then(() => {
setCurrentHandle(handle);
})
.catch((err) => {
message.error((err as Error).message);
});
}
},
onCancel() {
setCurrentHandle(handle);
}
});
};

const handleSetChanged = (changed: boolean) => {
setChanged(changed);
};

const handleSave = (md: string) => {
if (currentHandle) {
writeFile(currentHandle, md).catch((err) => {
message.error((err as Error).message);
});
writeFile(currentHandle, md)
.then(() => {
setChanged(false);
})
.catch((err) => {
message.error((err as Error).message);
});
}
};

return (
<div className={styles.content}>
{handle instanceof FileSystemDirectoryHandle ? (
<Sidebar handle={handle} onSelect={handleSelect} />
<Sidebar handle={handle} changed={changed} onSelect={handleSelect} />
) : null}

<div className={styles.mdEditor}>
<MDEditor markdown={markdown} onSave={handleSave} />
<MDEditor
ref={editorRef}
markdown={markdown}
changed={changed}
onChanged={handleSetChanged}
onSave={handleSave}
/>
</div>
</div>
);
Expand Down
92 changes: 52 additions & 40 deletions src/filehandle/md_editor/component/MDEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useMemo, useRef, useState } from 'react';
import { forwardRef, useImperativeHandle, useMemo, useRef } from 'react';

import styles from './styles/MDEditor.module.less';
import { reduce } from './theme-reduce';
Expand Down Expand Up @@ -36,9 +36,15 @@ import { /* callCommand, */ getMarkdown /* insert */ } from '@milkdown/utils';

interface IMDEditorProps {
markdown?: string;
changed: boolean;
onChanged(changed: boolean): any;
onSave(markdown: string): any;
}

export interface IMDEditorExpose {
getMarkdown(): string;
}

function createMDEditor(
el: HTMLElement,
value = '',
Expand Down Expand Up @@ -85,52 +91,58 @@ function createMDEditor(

const getMDString = getMarkdown();

const MDEditor: React.FC<IMDEditorProps> = (props) => {
const { markdown = '', onSave } = props;
const MDEditor = forwardRef<IMDEditorExpose, IMDEditorProps>(
function MDEditor(props, ref) {
const { markdown = '', changed, onChanged, onSave } = props;

const editorContainerRef = useRef<HTMLDivElement>(null);
const editorRef = useRef<Editor>();
const mdStringRef = useRef('');
const editorContainerRef = useRef<HTMLDivElement>(null);
const editorRef = useRef<Editor>();
const mdStringRef = useRef('');

const [changed, setChanged] = useState(false);

useMemo(() => {
(async () => {
if (editorRef.current) {
await editorRef.current.destroy(true);
useImperativeHandle(ref, () => ({
getMarkdown() {
return editorRef.current ? getMDString(editorRef.current.ctx) : '';
}
}));

setChanged(false);
createMDEditor(editorContainerRef.current!, markdown, onUpdate).then(
(value) => {
editorRef.current = value;
mdStringRef.current = markdown;
useMemo(() => {
(async () => {
if (editorRef.current) {
await editorRef.current.destroy(true);
}
);
})();
}, [markdown]);

function onUpdate(md: string) {
setChanged(true);
mdStringRef.current = md;
}
createMDEditor(editorContainerRef.current!, markdown, onUpdate).then(
(value) => {
editorRef.current = value;
mdStringRef.current = markdown;
onChanged(false);
}
);
})();
}, [markdown]);

function onUpdate(md: string) {
onChanged(true);
mdStringRef.current = md;
}

const handleSave = (e: React.KeyboardEvent<HTMLDivElement>) => {
if (e.ctrlKey && e.key.toLocaleLowerCase() === 's') {
e.preventDefault();
const handleSave = (e: React.KeyboardEvent<HTMLDivElement>) => {
if (e.ctrlKey && e.key.toLocaleLowerCase() === 's') {
e.preventDefault();

changed && onSave(getMDString(editorRef.current!.ctx));
}
};

return (
<div
ref={editorContainerRef}
id="md-editor"
className={styles.editorContainer}
onKeyDown={handleSave}
></div>
);
};
changed && onSave(getMDString(editorRef.current!.ctx));
}
};

return (
<div
ref={editorContainerRef}
id="md-editor"
className={styles.editorContainer}
onKeyDown={handleSave}
></div>
);
}
);

export default MDEditor;
16 changes: 13 additions & 3 deletions src/filehandle/md_editor/component/MDSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import styles from './styles/MDSidebar.module.less';

interface ISidebarProps {
handle: DH;
changed: boolean;
onSelect(handle: FH): void;
}

Expand Down Expand Up @@ -57,10 +58,12 @@ async function getDeepChildren(handle: DH): Promise<ExtendFileInfo[]> {

function Menu({
activeId,
changed,
items,
onItemClick
}: {
activeId: string;
changed: boolean;
items: ExtendFileInfo[];
onItemClick: (file: ExtendFileInfo) => void;
}) {
Expand Down Expand Up @@ -94,7 +97,8 @@ function Menu({
className={classNames(styles.row, {
[styles.active]:
activeId === item.id && !expands.includes(activeId),
[styles.directory]: item.children
[styles.directory]: item.children,
[styles.changed]: changed
})}
onClick={
item.children
Expand All @@ -118,6 +122,7 @@ function Menu({
<Menu
items={item.children}
activeId={activeId}
changed={changed}
onItemClick={onItemClick}
/>
) : null}
Expand All @@ -128,7 +133,7 @@ function Menu({
}

export const Sidebar: React.FC<Readonly<ISidebarProps>> = (props) => {
const { handle, onSelect } = props;
const { handle, changed, onSelect } = props;

const { message } = App.useApp();

Expand All @@ -154,7 +159,12 @@ export const Sidebar: React.FC<Readonly<ISidebarProps>> = (props) => {

return (
<Sider className={styles.sidebar} width={250}>
<Menu items={list} onItemClick={handleSelect} activeId={activeId} />
<Menu
items={list}
changed={changed}
activeId={activeId}
onItemClick={handleSelect}
/>
</Sider>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@
&:not(.directory):hover {
background-color: rgb(0 0 0 / 6%);
}

&.active.changed > span::after {
content: '*';
color: var(--color-primary);
}
}
}

Expand Down

0 comments on commit 9ea79e4

Please sign in to comment.