mirror of
https://github.com/usememos/memos.git
synced 2025-06-05 22:09:59 +02:00
fix: auto complete in memo editor (#720)
This commit is contained in:
@ -51,7 +51,7 @@ const ArchivedMemo: React.FC<Props> = (props: Props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`memo-wrapper archived-memo ${"memos-" + memo.id}`} onMouseLeave={handleMouseLeaveMemoWrapper}>
|
<div className={`memo-wrapper archived ${"memos-" + memo.id}`} onMouseLeave={handleMouseLeaveMemoWrapper}>
|
||||||
<div className="memo-top-wrapper">
|
<div className="memo-top-wrapper">
|
||||||
<span className="time-text">
|
<span className="time-text">
|
||||||
{t("common.archived-at")} {utils.getDateTimeString(memo.updatedTs)}
|
{t("common.archived-at")} {utils.getDateTimeString(memo.updatedTs)}
|
||||||
|
@ -5,6 +5,7 @@ import "../../less/editor.less";
|
|||||||
export interface EditorRefActions {
|
export interface EditorRefActions {
|
||||||
focus: FunctionType;
|
focus: FunctionType;
|
||||||
insertText: (text: string, prefix?: string, suffix?: string) => void;
|
insertText: (text: string, prefix?: string, suffix?: string) => void;
|
||||||
|
removeText: (start: number, length: number) => void;
|
||||||
setContent: (text: string) => void;
|
setContent: (text: string) => void;
|
||||||
getContent: () => string;
|
getContent: () => string;
|
||||||
getSelectedContent: () => string;
|
getSelectedContent: () => string;
|
||||||
@ -67,6 +68,19 @@ const Editor = forwardRef(function Editor(props: Props, ref: React.ForwardedRef<
|
|||||||
handleContentChangeCallback(editorRef.current.value);
|
handleContentChangeCallback(editorRef.current.value);
|
||||||
refresh();
|
refresh();
|
||||||
},
|
},
|
||||||
|
removeText: (start: number, length: number) => {
|
||||||
|
if (!editorRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const prevValue = editorRef.current.value;
|
||||||
|
const value = prevValue.slice(0, start) + prevValue.slice(start + length);
|
||||||
|
editorRef.current.value = value;
|
||||||
|
editorRef.current.focus();
|
||||||
|
editorRef.current.selectionEnd = start;
|
||||||
|
handleContentChangeCallback(editorRef.current.value);
|
||||||
|
refresh();
|
||||||
|
},
|
||||||
setContent: (text: string) => {
|
setContent: (text: string) => {
|
||||||
if (editorRef.current) {
|
if (editorRef.current) {
|
||||||
editorRef.current.value = text;
|
editorRef.current.value = text;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { toLower } from "lodash";
|
import { last, toLower } from "lodash";
|
||||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { deleteMemoResource, upsertMemoResource } from "../helpers/api";
|
import { deleteMemoResource, upsertMemoResource } from "../helpers/api";
|
||||||
@ -14,6 +14,8 @@ import ResourceIcon from "./ResourceIcon";
|
|||||||
import showResourcesSelectorDialog from "./ResourcesSelectorDialog";
|
import showResourcesSelectorDialog from "./ResourcesSelectorDialog";
|
||||||
import "../less/memo-editor.less";
|
import "../less/memo-editor.less";
|
||||||
|
|
||||||
|
const listItemSymbolList = ["* ", "- ", "- [ ] ", "- [x] ", "- [X] "];
|
||||||
|
|
||||||
const getEditorContentCache = (): string => {
|
const getEditorContentCache = (): string => {
|
||||||
return storage.get(["editorContentCache"]).editorContentCache ?? "";
|
return storage.get(["editorContentCache"]).editorContentCache ?? "";
|
||||||
};
|
};
|
||||||
@ -48,7 +50,7 @@ const MemoEditor = () => {
|
|||||||
shouldShowEmojiPicker: false,
|
shouldShowEmojiPicker: false,
|
||||||
});
|
});
|
||||||
const [allowSave, setAllowSave] = useState<boolean>(false);
|
const [allowSave, setAllowSave] = useState<boolean>(false);
|
||||||
const prevGlobalStateRef = useRef(editorState);
|
const prevEditorStateRef = useRef(editorState);
|
||||||
const editorRef = useRef<EditorRefActions>(null);
|
const editorRef = useRef<EditorRefActions>(null);
|
||||||
const tagSelectorRef = useRef<HTMLDivElement>(null);
|
const tagSelectorRef = useRef<HTMLDivElement>(null);
|
||||||
const memoVisibilityOptionSelectorItems = VISIBILITY_SELECTOR_ITEMS.map((item) => {
|
const memoVisibilityOptionSelectorItems = VISIBILITY_SELECTOR_ITEMS.map((item) => {
|
||||||
@ -87,10 +89,14 @@ const MemoEditor = () => {
|
|||||||
storage.remove(["editingMemoIdCache"]);
|
storage.remove(["editingMemoIdCache"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
prevGlobalStateRef.current = editorState;
|
prevEditorStateRef.current = editorState;
|
||||||
}, [editorState.editMemoId]);
|
}, [editorState.editMemoId]);
|
||||||
|
|
||||||
const handleKeyDown = (event: React.KeyboardEvent) => {
|
const handleKeyDown = (event: React.KeyboardEvent) => {
|
||||||
|
if (!editorRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (event.ctrlKey || event.metaKey) {
|
if (event.ctrlKey || event.metaKey) {
|
||||||
if (event.key === "Enter") {
|
if (event.key === "Enter") {
|
||||||
handleSaveBtnClick();
|
handleSaveBtnClick();
|
||||||
@ -98,39 +104,36 @@ const MemoEditor = () => {
|
|||||||
}
|
}
|
||||||
if (event.key === "b") {
|
if (event.key === "b") {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
editorRef.current?.insertText("", "**", "**");
|
editorRef.current.insertText("", "**", "**");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (event.key === "i") {
|
if (event.key === "i") {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
editorRef.current?.insertText("", "*", "*");
|
editorRef.current.insertText("", "*", "*");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (event.key === "e") {
|
if (event.key === "e") {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
editorRef.current?.insertText("", "`", "`");
|
editorRef.current.insertText("", "`", "`");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.key === "Enter") {
|
if (event.key === "Enter") {
|
||||||
if (!editorRef.current) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const cursorPosition = editorRef.current.getCursorPosition();
|
const cursorPosition = editorRef.current.getCursorPosition();
|
||||||
const prevValue = editorRef.current.getContent().slice(0, cursorPosition);
|
const contentBeforeCursor = editorRef.current.getContent().slice(0, cursorPosition);
|
||||||
const prevRows = prevValue.split("\n");
|
const rowValue = last(contentBeforeCursor.split("\n"));
|
||||||
const prevRowValue = prevRows[prevRows.length - 1];
|
if (rowValue) {
|
||||||
if (prevRowValue === "- " || prevRowValue === "- [ ] " || prevRowValue === "- [x] " || prevRowValue === "- [X] ") {
|
if (listItemSymbolList.includes(rowValue)) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
prevRows[prevRows.length - 1] = "";
|
editorRef.current.removeText(cursorPosition - rowValue.length, rowValue.length);
|
||||||
editorRef.current.setContent(prevRows.join("\n"));
|
|
||||||
} else {
|
} else {
|
||||||
if (prevRowValue.startsWith("- [ ] ") || prevRowValue.startsWith("- [x] ") || prevRowValue.startsWith("- [X] ")) {
|
for (const listItemSymbol of listItemSymbolList) {
|
||||||
|
if (rowValue.startsWith(listItemSymbol)) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
editorRef.current.insertText("", "\n- [ ] ");
|
editorRef.current.insertText("", `\n${listItemSymbol}`);
|
||||||
} else if (prevRowValue.startsWith("- ")) {
|
}
|
||||||
event.preventDefault();
|
}
|
||||||
editorRef.current.insertText("", "\n- ");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@ -138,14 +141,14 @@ const MemoEditor = () => {
|
|||||||
if (event.key === "Escape") {
|
if (event.key === "Escape") {
|
||||||
if (state.fullscreen) {
|
if (state.fullscreen) {
|
||||||
handleFullscreenBtnClick();
|
handleFullscreenBtnClick();
|
||||||
} else {
|
} else if (editorState.editMemoId) {
|
||||||
handleCancelEdit();
|
handleCancelEdit();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (event.key === "Tab") {
|
if (event.key === "Tab") {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
editorRef.current?.insertText(" ".repeat(TAB_SPACE_WIDTH));
|
editorRef.current.insertText(" ".repeat(TAB_SPACE_WIDTH));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -251,11 +254,13 @@ const MemoEditor = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleCancelEdit = () => {
|
const handleCancelEdit = () => {
|
||||||
|
if (editorState.editMemoId) {
|
||||||
editorStateService.clearEditMemo();
|
editorStateService.clearEditMemo();
|
||||||
editorStateService.clearResourceList();
|
editorStateService.clearResourceList();
|
||||||
editorRef.current?.setContent("");
|
editorRef.current?.setContent("");
|
||||||
setEditorContentCache("");
|
setEditorContentCache("");
|
||||||
storage.remove(["editingMemoVisibilityCache"]);
|
storage.remove(["editingMemoVisibilityCache"]);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleContentChange = (content: string) => {
|
const handleContentChange = (content: string) => {
|
||||||
|
@ -117,20 +117,16 @@ const SystemSection = () => {
|
|||||||
<span className="normal-text">
|
<span className="normal-text">
|
||||||
{t("setting.system-section.database-file-size")}: <span className="font-mono font-medium">{formatBytes(state.dbSize)}</span>
|
{t("setting.system-section.database-file-size")}: <span className="font-mono font-medium">{formatBytes(state.dbSize)}</span>
|
||||||
</span>
|
</span>
|
||||||
<Button size="sm" onClick={handleVacuumBtnClick}>
|
<Button onClick={handleVacuumBtnClick}>{t("common.vacuum")}</Button>
|
||||||
{t("common.vacuum")}
|
|
||||||
</Button>
|
|
||||||
</label>
|
</label>
|
||||||
<p className="title-text">{t("sidebar.setting")}</p>
|
<p className="title-text">{t("sidebar.setting")}</p>
|
||||||
<label className="form-label">
|
<label className="form-label">
|
||||||
<span className="normal-text">{t("setting.system-section.allow-user-signup")}</span>
|
<span className="normal-text">{t("setting.system-section.allow-user-signup")}</span>
|
||||||
<Switch size="sm" checked={state.allowSignUp} onChange={(event) => handleAllowSignUpChanged(event.target.checked)} />
|
<Switch checked={state.allowSignUp} onChange={(event) => handleAllowSignUpChanged(event.target.checked)} />
|
||||||
</label>
|
</label>
|
||||||
<div className="form-label">
|
<div className="form-label">
|
||||||
<span className="normal-text">{t("setting.system-section.additional-style")}</span>
|
<span className="normal-text">{t("setting.system-section.additional-style")}</span>
|
||||||
<Button size="sm" onClick={handleSaveAdditionalStyle}>
|
<Button onClick={handleSaveAdditionalStyle}>{t("common.save")}</Button>
|
||||||
{t("common.save")}
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
<Textarea
|
<Textarea
|
||||||
className="w-full"
|
className="w-full"
|
||||||
@ -139,16 +135,14 @@ const SystemSection = () => {
|
|||||||
fontSize: "14px",
|
fontSize: "14px",
|
||||||
}}
|
}}
|
||||||
minRows={4}
|
minRows={4}
|
||||||
maxRows={10}
|
maxRows={4}
|
||||||
placeholder={t("setting.system-section.additional-style-placeholder")}
|
placeholder={t("setting.system-section.additional-style-placeholder")}
|
||||||
value={state.additionalStyle}
|
value={state.additionalStyle}
|
||||||
onChange={(event) => handleAdditionalStyleChanged(event.target.value)}
|
onChange={(event) => handleAdditionalStyleChanged(event.target.value)}
|
||||||
/>
|
/>
|
||||||
<div className="form-label mt-2">
|
<div className="form-label mt-2">
|
||||||
<span className="normal-text">{t("setting.system-section.additional-script")}</span>
|
<span className="normal-text">{t("setting.system-section.additional-script")}</span>
|
||||||
<Button size="sm" onClick={handleSaveAdditionalScript}>
|
<Button onClick={handleSaveAdditionalScript}>{t("common.save")}</Button>
|
||||||
{t("common.save")}
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
<Textarea
|
<Textarea
|
||||||
className="w-full"
|
className="w-full"
|
||||||
@ -158,7 +152,7 @@ const SystemSection = () => {
|
|||||||
fontSize: "14px",
|
fontSize: "14px",
|
||||||
}}
|
}}
|
||||||
minRows={4}
|
minRows={4}
|
||||||
maxRows={10}
|
maxRows={4}
|
||||||
placeholder={t("setting.system-section.additional-script-placeholder")}
|
placeholder={t("setting.system-section.additional-script-placeholder")}
|
||||||
value={state.additionalScript}
|
value={state.additionalScript}
|
||||||
onChange={(event) => handleAdditionalScriptChanged(event.target.value)}
|
onChange={(event) => handleAdditionalScriptChanged(event.target.value)}
|
||||||
|
@ -5,6 +5,10 @@
|
|||||||
@apply border-gray-200 border-2 dark:border-zinc-600;
|
@apply border-gray-200 border-2 dark:border-zinc-600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.archived {
|
||||||
|
@apply border-gray-200 dark:border-zinc-600;
|
||||||
|
}
|
||||||
|
|
||||||
> .corner-container {
|
> .corner-container {
|
||||||
@apply absolute top-0 right-0 z-1;
|
@apply absolute top-0 right-0 z-1;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user