mirror of
https://github.com/usememos/memos.git
synced 2025-03-23 06:00:08 +01:00
chore: simplify memo editor component (#1079)
This commit is contained in:
parent
c28d35d8f7
commit
a997e1d10d
@ -31,7 +31,7 @@ const CreateTagDialog: React.FC<Props> = (props: Props) => {
|
|||||||
getTagSuggestionList().then(({ data }) => {
|
getTagSuggestionList().then(({ data }) => {
|
||||||
setSuggestTagNameList(data.data.filter((tag) => validateTagName(tag)));
|
setSuggestTagNameList(data.data.filter((tag) => validateTagName(tag)));
|
||||||
});
|
});
|
||||||
}, []);
|
}, [tagNameList]);
|
||||||
|
|
||||||
const handleTagNameInputKeyDown = (event: React.KeyboardEvent) => {
|
const handleTagNameInputKeyDown = (event: React.KeyboardEvent) => {
|
||||||
if (event.key === "Enter") {
|
if (event.key === "Enter") {
|
||||||
|
@ -17,7 +17,6 @@ import "../less/memo-editor.less";
|
|||||||
|
|
||||||
const listItemSymbolList = ["- [ ] ", "- [x] ", "- [X] ", "* ", "- "];
|
const listItemSymbolList = ["- [ ] ", "- [x] ", "- [X] ", "* ", "- "];
|
||||||
const emptyOlReg = /^(\d+)\. $/;
|
const emptyOlReg = /^(\d+)\. $/;
|
||||||
const pairSymbols = ["[]", "()", '""', "''", "{}", "``", "”“", "‘‘", "【】", "()", "《》"];
|
|
||||||
|
|
||||||
const getEditorContentCache = (): string => {
|
const getEditorContentCache = (): string => {
|
||||||
return storage.get(["editorContentCache"]).editorContentCache ?? "";
|
return storage.get(["editorContentCache"]).editorContentCache ?? "";
|
||||||
@ -29,12 +28,6 @@ const setEditorContentCache = (content: string) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const setEditingMemoVisibilityCache = (visibility: Visibility) => {
|
|
||||||
storage.set({
|
|
||||||
editingMemoVisibilityCache: visibility,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
fullscreen: boolean;
|
fullscreen: boolean;
|
||||||
isUploadingResource: boolean;
|
isUploadingResource: boolean;
|
||||||
@ -51,8 +44,8 @@ const MemoEditor = () => {
|
|||||||
const resourceStore = useResourceStore();
|
const resourceStore = useResourceStore();
|
||||||
|
|
||||||
const [state, setState] = useState<State>({
|
const [state, setState] = useState<State>({
|
||||||
isUploadingResource: false,
|
|
||||||
fullscreen: false,
|
fullscreen: false,
|
||||||
|
isUploadingResource: false,
|
||||||
isRequesting: false,
|
isRequesting: false,
|
||||||
});
|
});
|
||||||
const [allowSave, setAllowSave] = useState<boolean>(false);
|
const [allowSave, setAllowSave] = useState<boolean>(false);
|
||||||
@ -62,7 +55,6 @@ const MemoEditor = () => {
|
|||||||
const tagSelectorRef = useRef<HTMLDivElement>(null);
|
const tagSelectorRef = useRef<HTMLDivElement>(null);
|
||||||
const user = userStore.state.user as User;
|
const user = userStore.state.user as User;
|
||||||
const setting = user.setting;
|
const setting = user.setting;
|
||||||
const localSetting = user.localSetting;
|
|
||||||
const tags = tagStore.state.tags;
|
const tags = tagStore.state.tags;
|
||||||
const memoVisibilityOptionSelectorItems = VISIBILITY_SELECTOR_ITEMS.map((item) => {
|
const memoVisibilityOptionSelectorItems = VISIBILITY_SELECTOR_ITEMS.map((item) => {
|
||||||
return {
|
return {
|
||||||
@ -72,12 +64,9 @@ const MemoEditor = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const { editingMemoIdCache, editingMemoVisibilityCache } = storage.get(["editingMemoIdCache", "editingMemoVisibilityCache"]);
|
const { editingMemoIdCache } = storage.get(["editingMemoIdCache"]);
|
||||||
if (editingMemoIdCache) {
|
if (editingMemoIdCache) {
|
||||||
editorStore.setEditMemoWithId(editingMemoIdCache);
|
editorStore.setEditMemoWithId(editingMemoIdCache);
|
||||||
}
|
|
||||||
if (editingMemoVisibilityCache) {
|
|
||||||
editorStore.setMemoVisibility(editingMemoVisibilityCache as "PUBLIC" | "PROTECTED" | "PRIVATE");
|
|
||||||
} else {
|
} else {
|
||||||
editorStore.setMemoVisibility(setting.memoVisibility);
|
editorStore.setMemoVisibility(setting.memoVisibility);
|
||||||
}
|
}
|
||||||
@ -109,8 +98,7 @@ const MemoEditor = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const isMetaKey = event.ctrlKey || event.metaKey;
|
const isMetaKey = event.ctrlKey || event.metaKey;
|
||||||
const isShiftKey = event.shiftKey;
|
if (isMetaKey) {
|
||||||
if (!isShiftKey && isMetaKey) {
|
|
||||||
if (event.key === "Enter") {
|
if (event.key === "Enter") {
|
||||||
handleSaveBtnClick();
|
handleSaveBtnClick();
|
||||||
return;
|
return;
|
||||||
@ -141,8 +129,7 @@ const MemoEditor = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (event.key === "Enter") {
|
||||||
if (!isShiftKey && event.key === "Enter") {
|
|
||||||
const cursorPosition = editorRef.current.getCursorPosition();
|
const cursorPosition = editorRef.current.getCursorPosition();
|
||||||
const contentBeforeCursor = editorRef.current.getContent().slice(0, cursorPosition);
|
const contentBeforeCursor = editorRef.current.getContent().slice(0, cursorPosition);
|
||||||
const rowValue = last(contentBeforeCursor.split("\n"));
|
const rowValue = last(contentBeforeCursor.split("\n"));
|
||||||
@ -179,7 +166,7 @@ const MemoEditor = () => {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!isShiftKey && event.key === "Escape") {
|
if (event.key === "Escape") {
|
||||||
if (state.fullscreen) {
|
if (state.fullscreen) {
|
||||||
handleFullscreenBtnClick();
|
handleFullscreenBtnClick();
|
||||||
}
|
}
|
||||||
@ -190,57 +177,37 @@ const MemoEditor = () => {
|
|||||||
const tabSpace = " ".repeat(TAB_SPACE_WIDTH);
|
const tabSpace = " ".repeat(TAB_SPACE_WIDTH);
|
||||||
const cursorPosition = editorRef.current.getCursorPosition();
|
const cursorPosition = editorRef.current.getCursorPosition();
|
||||||
const selectedContent = editorRef.current.getSelectedContent();
|
const selectedContent = editorRef.current.getSelectedContent();
|
||||||
if (isShiftKey) {
|
|
||||||
const beforeContent = editorRef.current.getContent().slice(0, cursorPosition);
|
|
||||||
for (let i = beforeContent.length - 1; i >= 0; i--) {
|
|
||||||
if (beforeContent[i] !== "\n") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const rowStart = i + 1;
|
|
||||||
const isTabSpace = beforeContent.substring(rowStart, i + TAB_SPACE_WIDTH + 1) === tabSpace;
|
|
||||||
const isSpace = beforeContent.substring(rowStart, i + 2) === " ";
|
|
||||||
if (!isTabSpace && !isSpace) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const removeLength = isTabSpace ? TAB_SPACE_WIDTH : 1;
|
|
||||||
editorRef.current.removeText(rowStart, removeLength);
|
|
||||||
const startPos = cursorPosition - removeLength;
|
|
||||||
let endPos = startPos;
|
|
||||||
if (selectedContent) {
|
|
||||||
endPos += selectedContent.length;
|
|
||||||
}
|
|
||||||
editorRef.current.setCursorPosition(startPos, endPos);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
editorRef.current.insertText(tabSpace);
|
editorRef.current.insertText(tabSpace);
|
||||||
if (selectedContent) {
|
if (selectedContent) {
|
||||||
editorRef.current.setCursorPosition(cursorPosition + TAB_SPACE_WIDTH);
|
editorRef.current.setCursorPosition(cursorPosition + TAB_SPACE_WIDTH);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUploadResource = async (file: File) => {
|
||||||
|
setState((state) => {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
isUploadingResource: true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
let resource = undefined;
|
||||||
|
try {
|
||||||
|
resource = await resourceStore.createResourceWithBlob(file);
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error(error);
|
||||||
|
toastHelper.error(error.response.data.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (localSetting.enablePowerfulEditor) {
|
setState((state) => {
|
||||||
for (const symbol of pairSymbols) {
|
return {
|
||||||
if (event.key === symbol[0]) {
|
...state,
|
||||||
event.preventDefault();
|
isUploadingResource: false,
|
||||||
editorRef.current.insertText("", symbol[0], symbol[1]);
|
};
|
||||||
return;
|
});
|
||||||
}
|
return resource;
|
||||||
}
|
|
||||||
if (event.key === "Backspace") {
|
|
||||||
const cursor = editorRef.current.getCursorPosition();
|
|
||||||
const content = editorRef.current.getContent();
|
|
||||||
const deleteChar = content?.slice(cursor - 1, cursor);
|
|
||||||
const nextChar = content?.slice(cursor, cursor + 1);
|
|
||||||
if (pairSymbols.includes(`${deleteChar}${nextChar}`)) {
|
|
||||||
event.preventDefault();
|
|
||||||
editorRef.current.removeText(cursor - 1, 2);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const uploadMultiFiles = async (files: FileList) => {
|
const uploadMultiFiles = async (files: FileList) => {
|
||||||
@ -274,32 +241,6 @@ const MemoEditor = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleUploadResource = async (file: File) => {
|
|
||||||
setState((state) => {
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
isUploadingResource: true,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
let resource = undefined;
|
|
||||||
|
|
||||||
try {
|
|
||||||
resource = await resourceStore.createResourceWithBlob(file);
|
|
||||||
} catch (error: any) {
|
|
||||||
console.error(error);
|
|
||||||
toastHelper.error(error.response.data.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
setState((state) => {
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
isUploadingResource: false,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
return resource;
|
|
||||||
};
|
|
||||||
|
|
||||||
const scrollToEditingMemo = useCallback(() => {
|
const scrollToEditingMemo = useCallback(() => {
|
||||||
if (editorState.editMemoId) {
|
if (editorState.editMemoId) {
|
||||||
const memoElements = document.getElementsByClassName(`memos-${editorState.editMemoId}`);
|
const memoElements = document.getElementsByClassName(`memos-${editorState.editMemoId}`);
|
||||||
@ -369,9 +310,7 @@ const MemoEditor = () => {
|
|||||||
});
|
});
|
||||||
editorStore.clearResourceList();
|
editorStore.clearResourceList();
|
||||||
setEditorContentCache("");
|
setEditorContentCache("");
|
||||||
storage.remove(["editingMemoVisibilityCache"]);
|
|
||||||
editorRef.current?.setContent("");
|
editorRef.current?.setContent("");
|
||||||
|
|
||||||
scrollToEditingMemo();
|
scrollToEditingMemo();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -381,10 +320,8 @@ const MemoEditor = () => {
|
|||||||
editorStore.clearResourceList();
|
editorStore.clearResourceList();
|
||||||
editorRef.current?.setContent("");
|
editorRef.current?.setContent("");
|
||||||
setEditorContentCache("");
|
setEditorContentCache("");
|
||||||
storage.remove(["editingMemoVisibilityCache"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollToEditingMemo();
|
scrollToEditingMemo();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleContentChange = (content: string) => {
|
const handleContentChange = (content: string) => {
|
||||||
@ -439,11 +376,8 @@ const MemoEditor = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTagSelectorClick = useCallback((event: React.MouseEvent) => {
|
const handleTagSelectorClick = useCallback((tag: string) => {
|
||||||
if (tagSelectorRef.current !== event.target && tagSelectorRef.current?.contains(event.target as Node)) {
|
editorRef.current?.insertText(`#${tag} `);
|
||||||
editorRef.current?.insertText(`#${(event.target as HTMLElement).textContent} ` ?? "");
|
|
||||||
editorRef.current?.scrollToCursor();
|
|
||||||
}
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleDeleteResource = async (resourceId: ResourceId) => {
|
const handleDeleteResource = async (resourceId: ResourceId) => {
|
||||||
@ -456,7 +390,6 @@ const MemoEditor = () => {
|
|||||||
const handleMemoVisibilityOptionChanged = async (value: string) => {
|
const handleMemoVisibilityOptionChanged = async (value: string) => {
|
||||||
const visibilityValue = value as Visibility;
|
const visibilityValue = value as Visibility;
|
||||||
editorStore.setMemoVisibility(visibilityValue);
|
editorStore.setMemoVisibility(visibilityValue);
|
||||||
setEditingMemoVisibilityCache(visibilityValue);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleEditorFocus = () => {
|
const handleEditorFocus = () => {
|
||||||
@ -495,12 +428,12 @@ const MemoEditor = () => {
|
|||||||
<div className="common-tools-container">
|
<div className="common-tools-container">
|
||||||
<div className="action-btn tag-action">
|
<div className="action-btn tag-action">
|
||||||
<Icon.Hash className="icon-img" />
|
<Icon.Hash className="icon-img" />
|
||||||
<div ref={tagSelectorRef} className="tag-list" onClick={handleTagSelectorClick}>
|
<div ref={tagSelectorRef} className="tag-list">
|
||||||
{tags.length > 0 ? (
|
{tags.length > 0 ? (
|
||||||
tags.map((tag) => {
|
tags.map((tag) => {
|
||||||
return (
|
return (
|
||||||
<span className="item-container" key={tag}>
|
<span className="item-container" onClick={() => handleTagSelectorClick(tag)} key={tag}>
|
||||||
{tag}
|
#{tag}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
@ -16,7 +16,7 @@ const MemoResource: React.FC<Props> = (props: Props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={`w-auto flex flex-row justify-start items-center ${className}`}>
|
<div className={`w-auto flex flex-row justify-start items-center hover:opacity-80 ${className}`}>
|
||||||
{resource.type.startsWith("audio") ? (
|
{resource.type.startsWith("audio") ? (
|
||||||
<>
|
<>
|
||||||
<audio className="h-8" src={resourceUrl} controls></audio>
|
<audio className="h-8" src={resourceUrl} controls></audio>
|
||||||
@ -24,7 +24,7 @@ const MemoResource: React.FC<Props> = (props: Props) => {
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Icon.FileText className="w-4 h-auto mr-1 text-gray-500" />
|
<Icon.FileText className="w-4 h-auto mr-1 text-gray-500" />
|
||||||
<span className="text-gray-500 text-sm max-w-xs truncate font-mono cursor-pointer" onClick={handlePreviewBtnClick}>
|
<span className="text-gray-500 text-sm max-w-[256px] truncate font-mono cursor-pointer" onClick={handlePreviewBtnClick}>
|
||||||
{resource.filename}
|
{resource.filename}
|
||||||
</span>
|
</span>
|
||||||
</>
|
</>
|
||||||
|
@ -6,8 +6,6 @@ interface StorageData {
|
|||||||
editorContentCache: string;
|
editorContentCache: string;
|
||||||
// Editing memo id cache
|
// Editing memo id cache
|
||||||
editingMemoIdCache: MemoId;
|
editingMemoIdCache: MemoId;
|
||||||
// Editing memo visibility
|
|
||||||
editingMemoVisibilityCache: Visibility;
|
|
||||||
// locale
|
// locale
|
||||||
locale: Locale;
|
locale: Locale;
|
||||||
// appearance
|
// appearance
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
@apply flex flex-row justify-start items-center;
|
@apply flex flex-row justify-start items-center;
|
||||||
|
|
||||||
> .action-btn {
|
> .action-btn {
|
||||||
@apply flex flex-row justify-center items-center p-1 w-auto h-auto mr-1 select-none rounded cursor-pointer dark:text-gray-200 opacity-60 hover:opacity-90 hover:bg-gray-300 dark:hover:bg-zinc-800 hover:shadow;
|
@apply flex flex-row justify-center items-center p-1 w-auto h-auto mr-1 select-none rounded cursor-pointer text-gray-600 dark:text-gray-200 hover:bg-gray-300 dark:hover:bg-zinc-800 hover:shadow;
|
||||||
|
|
||||||
&.tag-action {
|
&.tag-action {
|
||||||
@apply relative;
|
@apply relative;
|
||||||
@ -49,14 +49,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
> .tag-list {
|
> .tag-list {
|
||||||
@apply hidden flex-col justify-start items-start absolute top-6 left-0 mt-1 p-1 z-1 rounded w-36 max-h-52 overflow-auto font-mono bg-black;
|
@apply hidden flex-row justify-start items-start flex-wrap absolute top-6 left-0 mt-1 p-1 z-1 rounded w-52 h-auto max-h-48 overflow-y-auto font-mono shadow bg-zinc-200 dark:bg-zinc-600;
|
||||||
|
|
||||||
> .item-container {
|
> .item-container {
|
||||||
@apply w-full text-white cursor-pointer rounded text-sm leading-6 px-2 truncate hover:bg-gray-700 shrink-0;
|
@apply w-auto max-w-full truncate text-black dark:text-gray-300 cursor-pointer rounded text-sm leading-6 px-2 truncate hover:bg-zinc-300 dark:hover:bg-zinc-700 shrink-0;
|
||||||
}
|
}
|
||||||
|
|
||||||
> .tip-text {
|
> .tip-text {
|
||||||
@apply w-full text-sm text-gray-200 leading-6 px-2 cursor-default;
|
@apply w-auto text-sm text-gray-200 leading-6 px-2 cursor-default;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user