mirror of
https://github.com/usememos/memos.git
synced 2025-06-05 22:09:59 +02:00
feat: layout style(1)
This commit is contained in:
@ -10,8 +10,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2"
|
||||||
"tiny-undo": "^0.0.8"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/lodash-es": "^4.17.5",
|
"@types/lodash-es": "^4.17.5",
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
import { forwardRef, ReactNode, useCallback, useContext, useEffect, useImperativeHandle, useRef } from "react";
|
import { forwardRef, ReactNode, useCallback, useEffect, useImperativeHandle, useRef } from "react";
|
||||||
import TinyUndo from "tiny-undo";
|
|
||||||
import appContext from "../../stores/appContext";
|
|
||||||
import { storage } from "../../helpers/storage";
|
|
||||||
import useRefresh from "../../hooks/useRefresh";
|
import useRefresh from "../../hooks/useRefresh";
|
||||||
import Only from "../common/OnlyWhen";
|
import Only from "../common/OnlyWhen";
|
||||||
import "../../less/editor.less";
|
import "../../less/editor.less";
|
||||||
@ -28,9 +25,6 @@ interface EditorProps {
|
|||||||
|
|
||||||
// eslint-disable-next-line react/display-name
|
// eslint-disable-next-line react/display-name
|
||||||
const Editor = forwardRef((props: EditorProps, ref: React.ForwardedRef<EditorRefActions>) => {
|
const Editor = forwardRef((props: EditorProps, ref: React.ForwardedRef<EditorRefActions>) => {
|
||||||
const {
|
|
||||||
globalState: { useTinyUndoHistoryCache },
|
|
||||||
} = useContext(appContext);
|
|
||||||
const {
|
const {
|
||||||
className,
|
className,
|
||||||
initialContent,
|
initialContent,
|
||||||
@ -42,59 +36,14 @@ const Editor = forwardRef((props: EditorProps, ref: React.ForwardedRef<EditorRef
|
|||||||
onContentChange: handleContentChangeCallback,
|
onContentChange: handleContentChangeCallback,
|
||||||
} = props;
|
} = props;
|
||||||
const editorRef = useRef<HTMLTextAreaElement>(null);
|
const editorRef = useRef<HTMLTextAreaElement>(null);
|
||||||
const tinyUndoRef = useRef<TinyUndo | null>(null);
|
|
||||||
// NOTE: auto-justify textarea height
|
|
||||||
const refresh = useRefresh();
|
const refresh = useRefresh();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!editorRef.current) {
|
if (editorRef.current && initialContent) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (initialContent) {
|
|
||||||
editorRef.current.value = initialContent;
|
editorRef.current.value = initialContent;
|
||||||
refresh();
|
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (useTinyUndoHistoryCache) {
|
|
||||||
if (!editorRef.current) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { tinyUndoActionsCache, tinyUndoIndexCache } = storage.get(["tinyUndoActionsCache", "tinyUndoIndexCache"]);
|
|
||||||
|
|
||||||
tinyUndoRef.current = new TinyUndo(editorRef.current, {
|
|
||||||
interval: 5000,
|
|
||||||
initialActions: tinyUndoActionsCache,
|
|
||||||
initialIndex: tinyUndoIndexCache,
|
|
||||||
});
|
|
||||||
|
|
||||||
tinyUndoRef.current.subscribe((actions, index) => {
|
|
||||||
storage.set({
|
|
||||||
tinyUndoActionsCache: actions,
|
|
||||||
tinyUndoIndexCache: index,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
tinyUndoRef.current?.destroy();
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
tinyUndoRef.current?.destroy();
|
|
||||||
tinyUndoRef.current = null;
|
|
||||||
storage.remove(["tinyUndoActionsCache", "tinyUndoIndexCache"]);
|
|
||||||
}
|
|
||||||
}, [useTinyUndoHistoryCache]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (editorRef.current) {
|
|
||||||
editorRef.current.style.height = "auto";
|
|
||||||
editorRef.current.style.height = (editorRef.current.scrollHeight ?? 0) + "px";
|
|
||||||
}
|
|
||||||
}, [editorRef.current?.value]);
|
|
||||||
|
|
||||||
useImperativeHandle(
|
useImperativeHandle(
|
||||||
ref,
|
ref,
|
||||||
() => ({
|
() => ({
|
||||||
@ -111,13 +60,11 @@ const Editor = forwardRef((props: EditorProps, ref: React.ForwardedRef<EditorRef
|
|||||||
editorRef.current.value =
|
editorRef.current.value =
|
||||||
prevValue.slice(0, editorRef.current.selectionStart) + rawText + prevValue.slice(editorRef.current.selectionStart);
|
prevValue.slice(0, editorRef.current.selectionStart) + rawText + prevValue.slice(editorRef.current.selectionStart);
|
||||||
handleContentChangeCallback(editorRef.current.value);
|
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;
|
||||||
handleContentChangeCallback(editorRef.current.value);
|
handleContentChangeCallback(editorRef.current.value);
|
||||||
refresh();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getContent: (): string => {
|
getContent: (): string => {
|
||||||
@ -140,7 +87,6 @@ const Editor = forwardRef((props: EditorProps, ref: React.ForwardedRef<EditorRef
|
|||||||
handleCommonConfirmBtnClick();
|
handleCommonConfirmBtnClick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
refresh();
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleCommonConfirmBtnClick = useCallback(() => {
|
const handleCommonConfirmBtnClick = useCallback(() => {
|
||||||
@ -150,9 +96,6 @@ const Editor = forwardRef((props: EditorProps, ref: React.ForwardedRef<EditorRef
|
|||||||
|
|
||||||
handleConfirmBtnClickCallback(editorRef.current.value);
|
handleConfirmBtnClickCallback(editorRef.current.value);
|
||||||
editorRef.current.value = "";
|
editorRef.current.value = "";
|
||||||
refresh();
|
|
||||||
// After confirm btn clicked, tiny-undo should reset state(clear actions and index)
|
|
||||||
tinyUndoRef.current?.resetState();
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleCommonCancelBtnClick = useCallback(() => {
|
const handleCommonCancelBtnClick = useCallback(() => {
|
||||||
|
@ -276,7 +276,7 @@ const MemoEditor: React.FC<Props> = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={"memo-editor-wrapper " + (showEditStatus ? "edit-ing" : "")}>
|
<div className={"memo-editor-container " + (showEditStatus ? "edit-ing" : "")}>
|
||||||
<p className={"tip-text " + (showEditStatus ? "" : "hidden")}>Editting...</p>
|
<p className={"tip-text " + (showEditStatus ? "" : "hidden")}>Editting...</p>
|
||||||
<Editor
|
<Editor
|
||||||
ref={editorRef}
|
ref={editorRef}
|
||||||
|
@ -6,7 +6,7 @@ import utils from "../helpers/utils";
|
|||||||
import { checkShouldShowMemoWithFilters } from "../helpers/filter";
|
import { checkShouldShowMemoWithFilters } from "../helpers/filter";
|
||||||
import Memo from "./Memo";
|
import Memo from "./Memo";
|
||||||
import toastHelper from "./Toast";
|
import toastHelper from "./Toast";
|
||||||
import "../less/memolist.less";
|
import "../less/memo-list.less";
|
||||||
|
|
||||||
interface Props {}
|
interface Props {}
|
||||||
|
|
||||||
@ -105,7 +105,7 @@ const MemoList: React.FC<Props> = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`memolist-wrapper ${isFetching ? "" : "completed"}`} onClick={handleMemoListClick} ref={wrapperElement}>
|
<div className={`memo-list-container ${isFetching ? "" : "completed"}`} onClick={handleMemoListClick} ref={wrapperElement}>
|
||||||
{shownMemos.map((memo) => (
|
{shownMemos.map((memo) => (
|
||||||
<Memo key={`${memo.id}-${memo.updatedAt}`} memo={memo} />
|
<Memo key={`${memo.id}-${memo.updatedAt}`} memo={memo} />
|
||||||
))}
|
))}
|
||||||
|
@ -33,7 +33,7 @@ const MenuBtnsPopup: React.FC<Props> = (props: Props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleMemosTrashBtnClick = () => {
|
const handleMemosTrashBtnClick = () => {
|
||||||
locationService.pushHistory("/recycle");
|
locationService.pushHistory("/trash");
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAboutBtnClick = () => {
|
const handleAboutBtnClick = () => {
|
||||||
|
@ -9,16 +9,10 @@ interface Props {}
|
|||||||
|
|
||||||
const PreferencesSection: React.FC<Props> = () => {
|
const PreferencesSection: React.FC<Props> = () => {
|
||||||
const { globalState } = useContext(appContext);
|
const { globalState } = useContext(appContext);
|
||||||
const { useTinyUndoHistoryCache, shouldHideImageUrl, shouldSplitMemoWord, shouldUseMarkdownParser } = globalState;
|
const { shouldHideImageUrl, shouldSplitMemoWord, shouldUseMarkdownParser } = globalState;
|
||||||
|
|
||||||
const demoMemoContent = "👋 Hiya, welcome to memos!\n* ✨ **Open source project**;\n* 😋 What do you think;\n* 📑 Tell me something plz;";
|
const demoMemoContent = "👋 Hiya, welcome to memos!\n* ✨ **Open source project**;\n* 😋 What do you think;\n* 📑 Tell me something plz;";
|
||||||
|
|
||||||
const handleOpenTinyUndoChanged = () => {
|
|
||||||
globalStateService.setAppSetting({
|
|
||||||
useTinyUndoHistoryCache: !useTinyUndoHistoryCache,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSplitWordsValueChanged = () => {
|
const handleSplitWordsValueChanged = () => {
|
||||||
globalStateService.setAppSetting({
|
globalStateService.setAppSetting({
|
||||||
shouldSplitMemoWord: !shouldSplitMemoWord,
|
shouldSplitMemoWord: !shouldSplitMemoWord,
|
||||||
@ -83,18 +77,6 @@ const PreferencesSection: React.FC<Props> = () => {
|
|||||||
<img className="icon-img" src={shouldHideImageUrl ? "/icons/checkbox-active.svg" : "/icons/checkbox.svg"} />
|
<img className="icon-img" src={shouldHideImageUrl ? "/icons/checkbox-active.svg" : "/icons/checkbox.svg"} />
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="section-container preferences-section-container">
|
|
||||||
<p className="title-text">Editor Extensions</p>
|
|
||||||
<label className="form-label checkbox-form-label" onClick={handleOpenTinyUndoChanged}>
|
|
||||||
<span className="normal-text">
|
|
||||||
Use{" "}
|
|
||||||
<a target="_blank" href="https://github.com/boojack/tiny-undo" onClick={(e) => e.stopPropagation()} rel="noreferrer">
|
|
||||||
tiny-undo
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
<img className="icon-img" src={useTinyUndoHistoryCache ? "/icons/checkbox-active.svg" : "/icons/checkbox.svg"} />
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div className="section-container">
|
<div className="section-container">
|
||||||
<p className="title-text">Others</p>
|
<p className="title-text">Others</p>
|
||||||
<div className="w-full flex flex-row justify-start items-center">
|
<div className="w-full flex flex-row justify-start items-center">
|
||||||
|
@ -67,11 +67,14 @@ const ShortcutContainer: React.FC<ShortcutContainerProps> = (props: ShortcutCont
|
|||||||
const { shortcut, isActive } = props;
|
const { shortcut, isActive } = props;
|
||||||
const [showConfirmDeleteBtn, toggleConfirmDeleteBtn] = useToggle(false);
|
const [showConfirmDeleteBtn, toggleConfirmDeleteBtn] = useToggle(false);
|
||||||
|
|
||||||
const handleQueryClick = () => {
|
console.log(props);
|
||||||
|
|
||||||
|
const handleShortcutClick = () => {
|
||||||
|
console.log("here");
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
locationService.setMemoShortcut("");
|
locationService.setMemoShortcut("");
|
||||||
} else {
|
} else {
|
||||||
if (!["/", "/recycle"].includes(locationService.getState().pathname)) {
|
if (!["/", "/trash"].includes(locationService.getState().pathname)) {
|
||||||
locationService.setPathname("/");
|
locationService.setPathname("/");
|
||||||
}
|
}
|
||||||
locationService.setMemoShortcut(shortcut.id);
|
locationService.setMemoShortcut(shortcut.id);
|
||||||
@ -125,7 +128,7 @@ const ShortcutContainer: React.FC<ShortcutContainerProps> = (props: ShortcutCont
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={`shortcut-container ${isActive ? "active" : ""}`} onClick={handleQueryClick}>
|
<div className={`shortcut-container ${isActive ? "active" : ""}`} onClick={handleShortcutClick}>
|
||||||
<div className="shortcut-text-container">
|
<div className="shortcut-text-container">
|
||||||
<span className="icon-text">#</span>
|
<span className="icon-text">#</span>
|
||||||
<span className="shortcut-text">{shortcut.title}</span>
|
<span className="shortcut-text">{shortcut.title}</span>
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { useRef } from "react";
|
import { useRef } from "react";
|
||||||
import UserBanner from "./UserBanner";
|
import UserBanner from "./UserBanner";
|
||||||
import ShortcutList from "./ShortcutList";
|
import ShortcutList from "./ShortcutList";
|
||||||
import TagList from "./TagList";
|
|
||||||
import UsageHeatMap from "./UsageHeatMap";
|
import UsageHeatMap from "./UsageHeatMap";
|
||||||
import "../less/siderbar.less";
|
import "../less/siderbar.less";
|
||||||
|
|
||||||
@ -15,7 +14,6 @@ const Sidebar: React.FC<Props> = () => {
|
|||||||
<UserBanner />
|
<UserBanner />
|
||||||
<UsageHeatMap />
|
<UsageHeatMap />
|
||||||
<ShortcutList />
|
<ShortcutList />
|
||||||
<TagList />
|
|
||||||
</aside>
|
</aside>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -101,7 +101,7 @@ const TagItemContainer: React.FC<TagItemContainerProps> = (props: TagItemContain
|
|||||||
locationService.setTagQuery("");
|
locationService.setTagQuery("");
|
||||||
} else {
|
} else {
|
||||||
utils.copyTextToClipboard(`#${tag.text} `);
|
utils.copyTextToClipboard(`#${tag.text} `);
|
||||||
if (!["/", "/recycle"].includes(locationService.getState().pathname)) {
|
if (!["/", "/trash"].includes(locationService.getState().pathname)) {
|
||||||
locationService.setPathname("/");
|
locationService.setPathname("/");
|
||||||
}
|
}
|
||||||
locationService.setTagQuery(tag.text);
|
locationService.setTagQuery(tag.text);
|
||||||
|
@ -76,7 +76,7 @@ const UsageHeatMap: React.FC<Props> = () => {
|
|||||||
locationService.setFromAndToQuery(0, 0);
|
locationService.setFromAndToQuery(0, 0);
|
||||||
setCurrentStat(null);
|
setCurrentStat(null);
|
||||||
} else if (item.count > 0) {
|
} else if (item.count > 0) {
|
||||||
if (!["/", "/recycle"].includes(locationService.getState().pathname)) {
|
if (!["/", "/trash"].includes(locationService.getState().pathname)) {
|
||||||
locationService.setPathname("/");
|
locationService.setPathname("/");
|
||||||
}
|
}
|
||||||
locationService.setFromAndToQuery(item.timestamp, item.timestamp + DAILY_TIMESTAMP);
|
locationService.setFromAndToQuery(item.timestamp, item.timestamp + DAILY_TIMESTAMP);
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import { InputAction } from "tiny-undo";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define storage data type
|
* Define storage data type
|
||||||
*/
|
*/
|
||||||
@ -9,14 +7,6 @@ interface StorageData {
|
|||||||
shouldSplitMemoWord: boolean;
|
shouldSplitMemoWord: boolean;
|
||||||
shouldHideImageUrl: boolean;
|
shouldHideImageUrl: boolean;
|
||||||
shouldUseMarkdownParser: boolean;
|
shouldUseMarkdownParser: boolean;
|
||||||
|
|
||||||
// Editor setting
|
|
||||||
useTinyUndoHistoryCache: boolean;
|
|
||||||
|
|
||||||
// tiny undo actions cache
|
|
||||||
tinyUndoActionsCache: InputAction[];
|
|
||||||
// tiny undo index cache
|
|
||||||
tinyUndoIndexCache: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type StorageKey = keyof StorageData;
|
type StorageKey = keyof StorageData;
|
||||||
|
@ -2,18 +2,10 @@
|
|||||||
|
|
||||||
.common-editor-wrapper {
|
.common-editor-wrapper {
|
||||||
.flex(column, flex-start, flex-start);
|
.flex(column, flex-start, flex-start);
|
||||||
position: relative;
|
@apply relative w-full h-full bg-white grow;
|
||||||
width: 100%;
|
|
||||||
height: auto;
|
|
||||||
background-color: white;
|
|
||||||
|
|
||||||
> .common-editor-inputer {
|
> .common-editor-inputer {
|
||||||
display: inline-block;
|
@apply w-full h-full pt-1 pb-3 grow;
|
||||||
width: 100%;
|
|
||||||
padding-top: 4px;
|
|
||||||
padding-bottom: 12px;
|
|
||||||
min-height: 40px;
|
|
||||||
max-height: 300px;
|
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
resize: none;
|
resize: none;
|
||||||
@ -22,7 +14,6 @@
|
|||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
.hide-scroll-bar();
|
|
||||||
|
|
||||||
&::placeholder {
|
&::placeholder {
|
||||||
padding-left: 2px;
|
padding-left: 2px;
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
body,
|
body,
|
||||||
html {
|
html {
|
||||||
@apply w-full h-full overflow-hidden text-base;
|
@apply w-screen h-screen overflow-hidden text-base;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Noto Sans", "Noto Sans CJK SC", "Microsoft YaHei UI", "Microsoft YaHei",
|
font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Noto Sans", "Noto Sans CJK SC", "Microsoft YaHei UI", "Microsoft YaHei",
|
||||||
"WenQuanYi Micro Hei", sans-serif, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
|
"WenQuanYi Micro Hei", sans-serif, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
|
||||||
"Noto Color Emoji";
|
"Noto Color Emoji";
|
||||||
|
@ -6,10 +6,5 @@
|
|||||||
|
|
||||||
#page-wrapper {
|
#page-wrapper {
|
||||||
.flex(row, flex-start, flex-start);
|
.flex(row, flex-start, flex-start);
|
||||||
@apply w-full max-w-4xl h-full m-auto -translate-x-4;
|
@apply w-full h-full m-auto;
|
||||||
|
|
||||||
> .content-wrapper {
|
|
||||||
.flex(column, flex-start, flex-start);
|
|
||||||
@apply relative flex-grow h-auto;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,7 @@
|
|||||||
@import "./mixin.less";
|
@import "./mixin.less";
|
||||||
|
|
||||||
.memo-editor-wrapper {
|
.memo-editor-container {
|
||||||
.flex(column, flex-start, flex-start);
|
@apply relative w-full h-96 max-h-full flex flex-col justify-start items-start grow bg-white p-4 rounded-lg border-2 border-gray-200;
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
height: auto;
|
|
||||||
background-color: white;
|
|
||||||
padding: 16px;
|
|
||||||
border-radius: 8px;
|
|
||||||
border: 2px solid @bg-gray;
|
|
||||||
|
|
||||||
&.edit-ing {
|
&.edit-ing {
|
||||||
border-color: @text-blue;
|
border-color: @text-blue;
|
||||||
|
@ -2,34 +2,18 @@
|
|||||||
|
|
||||||
.filter-query-container {
|
.filter-query-container {
|
||||||
.flex(row, flex-start, flex-start);
|
.flex(row, flex-start, flex-start);
|
||||||
width: 100%;
|
@apply w-full flex-wrap p-3 pb-1 text-sm leading-7;
|
||||||
flex-wrap: wrap;
|
|
||||||
padding: 12px 12px;
|
|
||||||
padding-bottom: 4px;
|
|
||||||
font-size: 13px;
|
|
||||||
line-height: 1.8;
|
|
||||||
|
|
||||||
> .tip-text {
|
> .tip-text {
|
||||||
padding: 2px 0;
|
@apply mr-2;
|
||||||
}
|
}
|
||||||
|
|
||||||
> .filter-item-container {
|
> .filter-item-container {
|
||||||
padding: 2px 8px;
|
@apply px-2 mr-2 cursor-pointer bg-gray-200 rounded whitespace-nowrap truncate hover:line-through;
|
||||||
margin-right: 6px;
|
|
||||||
cursor: pointer;
|
|
||||||
background-color: @bg-gray;
|
|
||||||
border-radius: 4px;
|
|
||||||
max-width: 200px;
|
max-width: 200px;
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
|
|
||||||
> .icon-text {
|
> .icon-text {
|
||||||
letter-spacing: 2px;
|
letter-spacing: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
|
||||||
text-decoration: line-through;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
@import "./mixin.less";
|
@import "./mixin.less";
|
||||||
|
|
||||||
.memolist-wrapper {
|
.memo-list-container {
|
||||||
.flex(column, flex-start, flex-start);
|
.flex(column, flex-start, flex-start);
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
width: 100%;
|
width: 100%;
|
@ -2,6 +2,7 @@
|
|||||||
@import "./memos-header.less";
|
@import "./memos-header.less";
|
||||||
|
|
||||||
.memo-trash-wrapper {
|
.memo-trash-wrapper {
|
||||||
|
@apply px-8;
|
||||||
.flex(column, flex-start, flex-start);
|
.flex(column, flex-start, flex-start);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -3,16 +3,7 @@
|
|||||||
|
|
||||||
.memo-wrapper {
|
.memo-wrapper {
|
||||||
.flex(column, flex-start, flex-start);
|
.flex(column, flex-start, flex-start);
|
||||||
width: 100%;
|
@apply w-full p-4 px-6 mt-2 bg-white rounded-lg border border-transparent hover:border-gray-200;
|
||||||
padding: 12px 18px;
|
|
||||||
background-color: white;
|
|
||||||
margin-top: 8px;
|
|
||||||
border-radius: 8px;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
border-color: @bg-gray;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .memo-top-wrapper {
|
> .memo-top-wrapper {
|
||||||
.flex(row, space-between, center);
|
.flex(row, space-between, center);
|
||||||
@ -21,10 +12,8 @@
|
|||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
|
|
||||||
> .time-text {
|
> .time-text {
|
||||||
font-size: 12px;
|
@apply text-xs;
|
||||||
line-height: 24px;
|
|
||||||
color: gray;
|
color: gray;
|
||||||
flex-shrink: 0;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,7 +103,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
> .memo-content-text {
|
> .memo-content-text {
|
||||||
width: 100%;
|
@apply w-full;
|
||||||
}
|
}
|
||||||
|
|
||||||
> .images-wrapper {
|
> .images-wrapper {
|
||||||
|
15
web/src/less/memos.less
Normal file
15
web/src/less/memos.less
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
@import "./mixin.less";
|
||||||
|
@import "./memos-header.less";
|
||||||
|
|
||||||
|
.memos-wrapper {
|
||||||
|
.flex(row, flex-start, flex-start);
|
||||||
|
@apply w-auto h-full grow;
|
||||||
|
|
||||||
|
> .memo-editor-wrapper {
|
||||||
|
@apply w-96 h-full shrink-0 py-16 px-8 bg-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .memo-list-wrapper {
|
||||||
|
@apply w-auto h-full grow px-16 flex flex-col justify-start items-start;
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
@import "./mixin.less";
|
@import "./mixin.less";
|
||||||
|
|
||||||
.search-bar-container {
|
.search-bar-container {
|
||||||
width: 160px;
|
@apply relative w-40;
|
||||||
|
|
||||||
> .search-bar-inputer {
|
> .search-bar-inputer {
|
||||||
.flex(row, flex-start, center);
|
.flex(row, flex-start, center);
|
||||||
@ -30,13 +30,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
> .quickly-action-wrapper {
|
> .quickly-action-wrapper {
|
||||||
display: none;
|
@apply hidden absolute top-9 -right-2 p-2 w-80;
|
||||||
position: absolute;
|
|
||||||
top: 52px;
|
|
||||||
right: -8px;
|
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
padding: 8px;
|
|
||||||
width: 320px;
|
|
||||||
|
|
||||||
> .quickly-action-container {
|
> .quickly-action-container {
|
||||||
.flex(column, flex-start, flex-start);
|
.flex(column, flex-start, flex-start);
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
.preference-wrapper {
|
.preference-wrapper {
|
||||||
.flex(column, flex-start, flex-start);
|
.flex(column, flex-start, flex-start);
|
||||||
@apply w-full h-full grow overflow-y-scroll;
|
@apply w-full h-full grow overflow-y-scroll px-8;
|
||||||
.hide-scroll-bar();
|
.hide-scroll-bar();
|
||||||
|
|
||||||
> .section-header-container {
|
> .section-header-container {
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
.sidebar-wrapper {
|
.sidebar-wrapper {
|
||||||
.flex(column, flex-start, flex-start);
|
.flex(column, flex-start, flex-start);
|
||||||
@apply w-60 h-auto py-4 overflow-x-hidden overflow-y-auto shrink-0;
|
@apply w-60 h-full py-4 overflow-x-hidden overflow-y-auto shrink-0;
|
||||||
.hide-scroll-bar();
|
.hide-scroll-bar();
|
||||||
|
|
||||||
> * {
|
> * {
|
||||||
|
@ -37,7 +37,7 @@ function Home() {
|
|||||||
{loadingState.isLoading ? null : (
|
{loadingState.isLoading ? null : (
|
||||||
<section id="page-wrapper">
|
<section id="page-wrapper">
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
<main className="content-wrapper">{homeRouterSwitch(pathname)}</main>
|
{homeRouterSwitch(pathname)}
|
||||||
</section>
|
</section>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -2,15 +2,20 @@ import MemoEditor from "../components/MemoEditor";
|
|||||||
import MemosHeader from "../components/MemosHeader";
|
import MemosHeader from "../components/MemosHeader";
|
||||||
import MemoFilter from "../components/MemoFilter";
|
import MemoFilter from "../components/MemoFilter";
|
||||||
import MemoList from "../components/MemoList";
|
import MemoList from "../components/MemoList";
|
||||||
|
import "../less/memos.less";
|
||||||
|
|
||||||
function Memos() {
|
function Memos() {
|
||||||
return (
|
return (
|
||||||
<>
|
<main className="memos-wrapper">
|
||||||
<MemosHeader />
|
<div className="memo-editor-wrapper">
|
||||||
<MemoEditor />
|
<MemoEditor />
|
||||||
<MemoFilter />
|
</div>
|
||||||
<MemoList />
|
<div className="memo-list-wrapper">
|
||||||
</>
|
<MemosHeader />
|
||||||
|
<MemoFilter />
|
||||||
|
<MemoList />
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ import "../less/memo-trash.less";
|
|||||||
|
|
||||||
interface Props {}
|
interface Props {}
|
||||||
|
|
||||||
const MemoTrash: React.FC<Props> = () => {
|
const Trash: React.FC<Props> = () => {
|
||||||
const {
|
const {
|
||||||
locationState: { query },
|
locationState: { query },
|
||||||
} = useContext(appContext);
|
} = useContext(appContext);
|
||||||
@ -126,4 +126,4 @@ const MemoTrash: React.FC<Props> = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MemoTrash;
|
export default Trash;
|
@ -1,9 +1,9 @@
|
|||||||
import Memos from "../pages/Memos";
|
import Memos from "../pages/Memos";
|
||||||
import MemoTrash from "../pages/MemoTrash";
|
import Trash from "../pages/Trash";
|
||||||
import Setting from "../pages/Setting";
|
import Setting from "../pages/Setting";
|
||||||
|
|
||||||
const homeRouter = {
|
const homeRouter = {
|
||||||
"/recycle": <MemoTrash />,
|
"/trash": <Trash />,
|
||||||
"/setting": <Setting />,
|
"/setting": <Setting />,
|
||||||
"*": <Memos />,
|
"*": <Memos />,
|
||||||
};
|
};
|
||||||
|
@ -4,12 +4,11 @@ import { AppSetting } from "../stores/globalStateStore";
|
|||||||
|
|
||||||
class GlobalStateService {
|
class GlobalStateService {
|
||||||
constructor() {
|
constructor() {
|
||||||
const cachedSetting = storage.get(["shouldSplitMemoWord", "shouldHideImageUrl", "shouldUseMarkdownParser", "useTinyUndoHistoryCache"]);
|
const cachedSetting = storage.get(["shouldSplitMemoWord", "shouldHideImageUrl", "shouldUseMarkdownParser"]);
|
||||||
const defaultAppSetting = {
|
const defaultAppSetting = {
|
||||||
shouldSplitMemoWord: cachedSetting.shouldSplitMemoWord ?? true,
|
shouldSplitMemoWord: cachedSetting.shouldSplitMemoWord ?? true,
|
||||||
shouldHideImageUrl: cachedSetting.shouldHideImageUrl ?? true,
|
shouldHideImageUrl: cachedSetting.shouldHideImageUrl ?? true,
|
||||||
shouldUseMarkdownParser: cachedSetting.shouldUseMarkdownParser ?? true,
|
shouldUseMarkdownParser: cachedSetting.shouldUseMarkdownParser ?? true,
|
||||||
useTinyUndoHistoryCache: cachedSetting.useTinyUndoHistoryCache ?? false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.setAppSetting(defaultAppSetting);
|
this.setAppSetting(defaultAppSetting);
|
||||||
|
@ -185,7 +185,7 @@ class LocationService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
public getValidPathname = (pathname: string): AppRouter => {
|
public getValidPathname = (pathname: string): AppRouter => {
|
||||||
if (["/", "/signin", "/recycle", "/setting"].includes(pathname)) {
|
if (["/", "/signin", "/trash", "/setting"].includes(pathname)) {
|
||||||
return pathname as AppRouter;
|
return pathname as AppRouter;
|
||||||
} else {
|
} else {
|
||||||
return "/";
|
return "/";
|
||||||
|
@ -82,6 +82,7 @@ class ShortcutService {
|
|||||||
public convertResponseModelShortcut(shortcut: Model.Shortcut): Model.Shortcut {
|
public convertResponseModelShortcut(shortcut: Model.Shortcut): Model.Shortcut {
|
||||||
return {
|
return {
|
||||||
...shortcut,
|
...shortcut,
|
||||||
|
id: String(shortcut.id),
|
||||||
createdAt: utils.getDataStringWithTs(shortcut.createdTs),
|
createdAt: utils.getDataStringWithTs(shortcut.createdTs),
|
||||||
updatedAt: utils.getDataStringWithTs(shortcut.updatedTs),
|
updatedAt: utils.getDataStringWithTs(shortcut.updatedTs),
|
||||||
};
|
};
|
||||||
|
@ -2,7 +2,6 @@ export interface AppSetting {
|
|||||||
shouldSplitMemoWord: boolean;
|
shouldSplitMemoWord: boolean;
|
||||||
shouldHideImageUrl: boolean;
|
shouldHideImageUrl: boolean;
|
||||||
shouldUseMarkdownParser: boolean;
|
shouldUseMarkdownParser: boolean;
|
||||||
useTinyUndoHistoryCache: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface State extends AppSetting {
|
export interface State extends AppSetting {
|
||||||
@ -71,5 +70,4 @@ export const defaultState: State = {
|
|||||||
shouldSplitMemoWord: true,
|
shouldSplitMemoWord: true,
|
||||||
shouldHideImageUrl: true,
|
shouldHideImageUrl: true,
|
||||||
shouldUseMarkdownParser: true,
|
shouldUseMarkdownParser: true,
|
||||||
useTinyUndoHistoryCache: false,
|
|
||||||
};
|
};
|
||||||
|
@ -165,7 +165,7 @@ export function reducer(state: State, action: Actions) {
|
|||||||
...state,
|
...state,
|
||||||
query: {
|
query: {
|
||||||
...state.query,
|
...state.query,
|
||||||
filter: action.payload,
|
shortcutId: action.payload,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
2
web/src/types/location.d.ts
vendored
2
web/src/types/location.d.ts
vendored
@ -11,7 +11,7 @@ interface Query {
|
|||||||
shortcutId: string;
|
shortcutId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type AppRouter = "/" | "/signin" | "/recycle" | "/setting";
|
type AppRouter = "/" | "/signin" | "/trash" | "/setting";
|
||||||
|
|
||||||
interface AppLocation {
|
interface AppLocation {
|
||||||
pathname: AppRouter;
|
pathname: AppRouter;
|
||||||
|
@ -2294,11 +2294,6 @@ text-table@^0.2.0:
|
|||||||
resolved "https://registry.nlark.com/text-table/download/text-table-0.2.0.tgz?cache=0&sync_timestamp=1618846790938&other_urls=https%3A%2F%2Fregistry.nlark.com%2Ftext-table%2Fdownload%2Ftext-table-0.2.0.tgz"
|
resolved "https://registry.nlark.com/text-table/download/text-table-0.2.0.tgz?cache=0&sync_timestamp=1618846790938&other_urls=https%3A%2F%2Fregistry.nlark.com%2Ftext-table%2Fdownload%2Ftext-table-0.2.0.tgz"
|
||||||
integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
|
integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
|
||||||
|
|
||||||
tiny-undo@^0.0.8:
|
|
||||||
version "0.0.8"
|
|
||||||
resolved "https://registry.npmjs.org/tiny-undo/-/tiny-undo-0.0.8.tgz"
|
|
||||||
integrity sha512-x/zUWVVoehq0TTLTLngUCvg41bssl5OYloNOE6sRz/2fsEetB87zpj4kEZyrmvzI3Rsq+t9olujqXprlu+lbvw==
|
|
||||||
|
|
||||||
to-fast-properties@^2.0.0:
|
to-fast-properties@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz"
|
resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz"
|
||||||
|
Reference in New Issue
Block a user