mirror of
https://github.com/usememos/memos.git
synced 2024-12-20 09:41:58 +03:00
feat: fullscreen editor
This commit is contained in:
parent
a2b32e0b75
commit
b85af714f5
1
web/public/icons/close-fullscreen.svg
Normal file
1
web/public/icons/close-fullscreen.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48"><path d="M6.1 44 4 41.9 18.9 27H9v-3h15v15h-3v-9.9ZM24 24V9h3v9.9L41.9 4 44 6.1 29.1 21H39v3Z"/></svg>
|
After Width: | Height: | Size: 165 B |
1
web/public/icons/open-fullscreen.svg
Normal file
1
web/public/icons/open-fullscreen.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48"><path d="M6 42V27h3v9.9L36.9 9H27V6h15v15h-3v-9.9L11.1 39H21v3Z"/></svg>
|
After Width: | Height: | Size: 135 B |
@ -15,6 +15,7 @@ interface EditorProps {
|
|||||||
className: string;
|
className: string;
|
||||||
initialContent: string;
|
initialContent: string;
|
||||||
placeholder: string;
|
placeholder: string;
|
||||||
|
fullscreen: boolean;
|
||||||
showConfirmBtn: boolean;
|
showConfirmBtn: boolean;
|
||||||
showCancelBtn: boolean;
|
showCancelBtn: boolean;
|
||||||
tools?: ReactNode;
|
tools?: ReactNode;
|
||||||
@ -29,6 +30,7 @@ const Editor = forwardRef((props: EditorProps, ref: React.ForwardedRef<EditorRef
|
|||||||
className,
|
className,
|
||||||
initialContent,
|
initialContent,
|
||||||
placeholder,
|
placeholder,
|
||||||
|
fullscreen,
|
||||||
showConfirmBtn,
|
showConfirmBtn,
|
||||||
showCancelBtn,
|
showCancelBtn,
|
||||||
onConfirmBtnClick: handleConfirmBtnClickCallback,
|
onConfirmBtnClick: handleConfirmBtnClickCallback,
|
||||||
@ -45,11 +47,11 @@ const Editor = forwardRef((props: EditorProps, ref: React.ForwardedRef<EditorRef
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (editorRef.current) {
|
if (editorRef.current && !fullscreen) {
|
||||||
editorRef.current.style.height = "auto";
|
editorRef.current.style.height = "auto";
|
||||||
editorRef.current.style.height = (editorRef.current.scrollHeight ?? 0) + "px";
|
editorRef.current.style.height = (editorRef.current.scrollHeight ?? 0) + "px";
|
||||||
}
|
}
|
||||||
}, [editorRef.current?.value]);
|
}, [editorRef.current?.value, fullscreen]);
|
||||||
|
|
||||||
useImperativeHandle(
|
useImperativeHandle(
|
||||||
ref,
|
ref,
|
||||||
|
@ -1,19 +1,26 @@
|
|||||||
import React, { useCallback, useEffect, useMemo, useRef } from "react";
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { UNKNOWN_ID } from "../helpers/consts";
|
import { UNKNOWN_ID } from "../helpers/consts";
|
||||||
import { editorStateService, locationService, memoService, resourceService } from "../services";
|
import { editorStateService, locationService, memoService, resourceService } from "../services";
|
||||||
import { useAppSelector } from "../store";
|
import { useAppSelector } from "../store";
|
||||||
import * as storage from "../helpers/storage";
|
import * as storage from "../helpers/storage";
|
||||||
import useToggle from "../hooks/useToggle";
|
|
||||||
import toastHelper from "./Toast";
|
import toastHelper from "./Toast";
|
||||||
import Editor, { EditorRefActions } from "./Editor/Editor";
|
import Editor, { EditorRefActions } from "./Editor/Editor";
|
||||||
import "../less/memo-editor.less";
|
import "../less/memo-editor.less";
|
||||||
|
|
||||||
interface Props {}
|
interface Props {}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
isUploadingResource: boolean;
|
||||||
|
fullscreen: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
const MemoEditor: React.FC<Props> = () => {
|
const MemoEditor: React.FC<Props> = () => {
|
||||||
const editorState = useAppSelector((state) => state.editor);
|
const editorState = useAppSelector((state) => state.editor);
|
||||||
const tags = useAppSelector((state) => state.memo.tags);
|
const tags = useAppSelector((state) => state.memo.tags);
|
||||||
const [isUploadingResource, setIsUploadingResource] = useToggle(false);
|
const [state, setState] = useState<State>({
|
||||||
|
isUploadingResource: false,
|
||||||
|
fullscreen: false,
|
||||||
|
});
|
||||||
const editorRef = useRef<EditorRefActions>(null);
|
const editorRef = useRef<EditorRefActions>(null);
|
||||||
const prevGlobalStateRef = useRef(editorState);
|
const prevGlobalStateRef = useRef(editorState);
|
||||||
const tagSeletorRef = useRef<HTMLDivElement>(null);
|
const tagSeletorRef = useRef<HTMLDivElement>(null);
|
||||||
@ -89,11 +96,14 @@ const MemoEditor: React.FC<Props> = () => {
|
|||||||
|
|
||||||
const handleUploadFile = useCallback(
|
const handleUploadFile = useCallback(
|
||||||
async (file: File) => {
|
async (file: File) => {
|
||||||
if (isUploadingResource) {
|
if (state.isUploadingResource) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsUploadingResource(true);
|
setState({
|
||||||
|
...state,
|
||||||
|
isUploadingResource: true,
|
||||||
|
});
|
||||||
const { type } = file;
|
const { type } = file;
|
||||||
|
|
||||||
if (!type.startsWith("image")) {
|
if (!type.startsWith("image")) {
|
||||||
@ -108,10 +118,13 @@ const MemoEditor: React.FC<Props> = () => {
|
|||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toastHelper.error(error);
|
toastHelper.error(error);
|
||||||
} finally {
|
} finally {
|
||||||
setIsUploadingResource(false);
|
setState({
|
||||||
|
...state,
|
||||||
|
isUploadingResource: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[isUploadingResource]
|
[state]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleSaveBtnClick = useCallback(async (content: string) => {
|
const handleSaveBtnClick = useCallback(async (content: string) => {
|
||||||
@ -175,6 +188,13 @@ const MemoEditor: React.FC<Props> = () => {
|
|||||||
inputEl.click();
|
inputEl.click();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const handleFullscreenBtnClick = () => {
|
||||||
|
setState({
|
||||||
|
...state,
|
||||||
|
fullscreen: !state.fullscreen,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const handleTagSeletorClick = useCallback((event: React.MouseEvent) => {
|
const handleTagSeletorClick = useCallback((event: React.MouseEvent) => {
|
||||||
if (tagSeletorRef.current !== event.target && tagSeletorRef.current?.contains(event.target as Node)) {
|
if (tagSeletorRef.current !== event.target && tagSeletorRef.current?.contains(event.target as Node)) {
|
||||||
editorRef.current?.insertText(`#${(event.target as HTMLElement).textContent} ` ?? "");
|
editorRef.current?.insertText(`#${(event.target as HTMLElement).textContent} ` ?? "");
|
||||||
@ -189,17 +209,18 @@ const MemoEditor: React.FC<Props> = () => {
|
|||||||
className: "memo-editor",
|
className: "memo-editor",
|
||||||
initialContent: getEditorContentCache(),
|
initialContent: getEditorContentCache(),
|
||||||
placeholder: "Any thoughts...",
|
placeholder: "Any thoughts...",
|
||||||
|
fullscreen: state.fullscreen,
|
||||||
showConfirmBtn: true,
|
showConfirmBtn: true,
|
||||||
showCancelBtn: isEditing,
|
showCancelBtn: isEditing,
|
||||||
onConfirmBtnClick: handleSaveBtnClick,
|
onConfirmBtnClick: handleSaveBtnClick,
|
||||||
onCancelBtnClick: handleCancelBtnClick,
|
onCancelBtnClick: handleCancelBtnClick,
|
||||||
onContentChange: handleContentChange,
|
onContentChange: handleContentChange,
|
||||||
}),
|
}),
|
||||||
[isEditing]
|
[isEditing, state.fullscreen]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={"memo-editor-container " + (isEditing ? "edit-ing" : "")}>
|
<div className={`memo-editor-container ${isEditing ? "edit-ing" : ""} ${state.fullscreen ? "fullscreen" : ""}`}>
|
||||||
<p className={"tip-text " + (isEditing ? "" : "hidden")}>Editting...</p>
|
<p className={"tip-text " + (isEditing ? "" : "hidden")}>Editting...</p>
|
||||||
<Editor
|
<Editor
|
||||||
ref={editorRef}
|
ref={editorRef}
|
||||||
@ -216,8 +237,11 @@ const MemoEditor: React.FC<Props> = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="action-btn">
|
<div className="action-btn">
|
||||||
<img className="icon-img" src="/icons/image.svg" onClick={handleUploadFileBtnClick} />
|
<img className="icon-img" src="/icons/image.svg" onClick={handleUploadFileBtnClick} />
|
||||||
<span className={`tip-text ${isUploadingResource ? "!block" : ""}`}>Uploading</span>
|
<span className={`tip-text ${state.isUploadingResource ? "!block" : ""}`}>Uploading</span>
|
||||||
</div>
|
</div>
|
||||||
|
<button className="action-btn" onClick={handleFullscreenBtnClick}>
|
||||||
|
<img className="icon-img" src={`/icons/${state.fullscreen ? "close" : "open"}-fullscreen.svg`} alt="" />
|
||||||
|
</button>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
@ -1,7 +1,20 @@
|
|||||||
@import "./mixin.less";
|
@import "./mixin.less";
|
||||||
|
|
||||||
.memo-editor-container {
|
.memo-editor-container {
|
||||||
@apply relative w-full max-h-full flex flex-col justify-start items-start bg-white p-4 rounded-lg border-2 border-gray-200;
|
@apply transition-all relative w-full max-h-full flex flex-col justify-start items-start bg-white p-4 rounded-lg border-2 border-gray-200;
|
||||||
|
|
||||||
|
&.fullscreen {
|
||||||
|
@apply fixed w-full h-full top-0 left-0 z-1000 border-none rounded-none sm:p-8;
|
||||||
|
background-color: #f6f5f4;
|
||||||
|
|
||||||
|
> .memo-editor {
|
||||||
|
@apply p-4 rounded-lg border shadow-lg flex flex-col flex-grow justify-start items-start relative w-full h-full bg-white;
|
||||||
|
|
||||||
|
> .common-editor-inputer {
|
||||||
|
@apply flex-grow w-full !h-full max-h-full;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&.edit-ing {
|
&.edit-ing {
|
||||||
border-color: @text-blue;
|
border-color: @text-blue;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
@import "./mixin.less";
|
@import "./mixin.less";
|
||||||
|
|
||||||
.sidebar-wrapper {
|
.sidebar-wrapper {
|
||||||
@apply fixed sm:sticky top-0 left-0 hidden sm:!flex flex-col justify-start items-start w-64 h-screen py-4 pl-2 z-10 bg-white sm:bg-transparent shadow-2xl sm:shadow-none overflow-x-hidden overflow-y-auto transition-all;
|
@apply fixed sm:sticky top-0 left-0 hidden sm:!flex flex-col justify-start items-start w-64 h-screen py-4 pl-2 z-10 sm:z-0 bg-white sm:bg-transparent shadow-2xl sm:shadow-none overflow-x-hidden overflow-y-auto transition-all;
|
||||||
.hide-scroll-bar();
|
.hide-scroll-bar();
|
||||||
|
|
||||||
> .close-container {
|
> .close-container {
|
||||||
|
Loading…
Reference in New Issue
Block a user