mirror of
https://github.com/usememos/memos.git
synced 2024-11-27 10:52:53 +03:00
parent
b96d78ed19
commit
cd7000da70
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Memos
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
@ -33,6 +33,5 @@
|
||||
"tailwindcss": "^3.0.18",
|
||||
"typescript": "^4.3.2",
|
||||
"vite": "^2.9.0"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
|
@ -150,11 +150,11 @@ const MemoCardDialog: React.FC<Props> = (props: Props) => {
|
||||
{linkMemos.length > 0 ? (
|
||||
<div className="linked-memos-wrapper">
|
||||
<p className="normal-text">{linkMemos.length} related MEMO</p>
|
||||
{linkMemos.map((m) => {
|
||||
const rawtext = parseHtmlToRawText(formatMemoContent(m.content)).replaceAll("\n", " ");
|
||||
{linkMemos.map((memo, index) => {
|
||||
const rawtext = parseHtmlToRawText(formatMemoContent(memo.content)).replaceAll("\n", " ");
|
||||
return (
|
||||
<div className="linked-memo-container" key={m.id} onClick={() => handleLinkedMemoClick(m)}>
|
||||
<span className="time-text">{m.dateStr} </span>
|
||||
<div className="linked-memo-container" key={`${index}-${memo.id}`} onClick={() => handleLinkedMemoClick(memo)}>
|
||||
<span className="time-text">{memo.dateStr} </span>
|
||||
{rawtext}
|
||||
</div>
|
||||
);
|
||||
@ -164,11 +164,11 @@ const MemoCardDialog: React.FC<Props> = (props: Props) => {
|
||||
{linkedMemos.length > 0 ? (
|
||||
<div className="linked-memos-wrapper">
|
||||
<p className="normal-text">{linkedMemos.length} linked MEMO</p>
|
||||
{linkedMemos.map((m) => {
|
||||
const rawtext = parseHtmlToRawText(formatMemoContent(m.content)).replaceAll("\n", " ");
|
||||
{linkedMemos.map((memo, index) => {
|
||||
const rawtext = parseHtmlToRawText(formatMemoContent(memo.content)).replaceAll("\n", " ");
|
||||
return (
|
||||
<div className="linked-memo-container" key={m.id} onClick={() => handleLinkedMemoClick(m)}>
|
||||
<span className="time-text">{m.dateStr} </span>
|
||||
<div className="linked-memo-container" key={`${index}-${memo.id}`} onClick={() => handleLinkedMemoClick(memo)}>
|
||||
<span className="time-text">{memo.dateStr} </span>
|
||||
{rawtext}
|
||||
</div>
|
||||
);
|
||||
|
@ -294,7 +294,7 @@ const MemoEditor: React.FC<Props> = () => {
|
||||
/>
|
||||
<div
|
||||
ref={tagSeletorRef}
|
||||
className={`tag-list ${isTagSeletorShown && tags.length > 0 ? "" : "hidden"}`}
|
||||
className={`tag-list ${isTagSeletorShown && tags.length > 0 ? "" : "!hidden"}`}
|
||||
onClick={handleTagSeletorClick}
|
||||
>
|
||||
{tags.map((t) => {
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { memoService, shortcutService } from "../services";
|
||||
import { useAppSelector } from "../store";
|
||||
import SearchBar from "./SearchBar";
|
||||
import { memoService, shortcutService } from "../services";
|
||||
import { toggleSiderbar } from "./Sidebar";
|
||||
import "../less/memos-header.less";
|
||||
|
||||
let prevRequestTimestamp = Date.now();
|
||||
@ -25,7 +26,7 @@ const MemosHeader: React.FC<Props> = () => {
|
||||
}
|
||||
}, [query, shortcuts]);
|
||||
|
||||
const handleMemoTextClick = useCallback(() => {
|
||||
const handleTitleTextClick = useCallback(() => {
|
||||
const now = Date.now();
|
||||
if (now - prevRequestTimestamp > 10 * 1000) {
|
||||
prevRequestTimestamp = now;
|
||||
@ -37,8 +38,13 @@ const MemosHeader: React.FC<Props> = () => {
|
||||
|
||||
return (
|
||||
<div className="section-header-container memos-header-container">
|
||||
<div className="title-text" onClick={handleMemoTextClick}>
|
||||
<span className="normal-text">{titleText}</span>
|
||||
<div className="title-container">
|
||||
<div className="action-btn" onClick={toggleSiderbar}>
|
||||
<img src="/icons/menu.svg" className="icon-img" alt="" />
|
||||
</div>
|
||||
<span className="title-text" onClick={handleTitleTextClick}>
|
||||
{titleText}
|
||||
</span>
|
||||
</div>
|
||||
<SearchBar />
|
||||
</div>
|
||||
|
@ -34,27 +34,31 @@ const SettingDialog: React.FC<Props> = (props: Props) => {
|
||||
</button>
|
||||
<div className="section-selector-container">
|
||||
<span className="section-title">Basic</span>
|
||||
<span
|
||||
onClick={() => handleSectionSelectorItemClick("my-account")}
|
||||
className={`section-item ${state.selectedSection === "my-account" ? "selected" : ""}`}
|
||||
>
|
||||
My account
|
||||
</span>
|
||||
<span
|
||||
onClick={() => handleSectionSelectorItemClick("preferences")}
|
||||
className={`section-item ${state.selectedSection === "preferences" ? "selected" : ""}`}
|
||||
>
|
||||
Preferences
|
||||
</span>
|
||||
<div className="section-items-container">
|
||||
<span
|
||||
onClick={() => handleSectionSelectorItemClick("my-account")}
|
||||
className={`section-item ${state.selectedSection === "my-account" ? "selected" : ""}`}
|
||||
>
|
||||
My account
|
||||
</span>
|
||||
<span
|
||||
onClick={() => handleSectionSelectorItemClick("preferences")}
|
||||
className={`section-item ${state.selectedSection === "preferences" ? "selected" : ""}`}
|
||||
>
|
||||
Preferences
|
||||
</span>
|
||||
</div>
|
||||
{user?.role === "OWNER" ? (
|
||||
<>
|
||||
<span className="section-title">Admin</span>
|
||||
<span
|
||||
onClick={() => handleSectionSelectorItemClick("member")}
|
||||
className={`section-item ${state.selectedSection === "member" ? "selected" : ""}`}
|
||||
>
|
||||
Member
|
||||
</span>
|
||||
<div className="section-items-container">
|
||||
<span
|
||||
onClick={() => handleSectionSelectorItemClick("member")}
|
||||
className={`section-item ${state.selectedSection === "member" ? "selected" : ""}`}
|
||||
>
|
||||
Member
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
|
@ -27,6 +27,11 @@ const Sidebar: React.FC<Props> = () => {
|
||||
|
||||
return (
|
||||
<aside className="sidebar-wrapper">
|
||||
<div className="close-container">
|
||||
<span className="action-btn" onClick={toggleSiderbar}>
|
||||
<img src="/icons/close.svg" className="icon-img" alt="" />
|
||||
</span>
|
||||
</div>
|
||||
<UserBanner />
|
||||
<div className="status-text-container">
|
||||
<div className="status-text memos-text">
|
||||
@ -57,4 +62,14 @@ const Sidebar: React.FC<Props> = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export const toggleSiderbar = () => {
|
||||
const sidebarEl = document.body.querySelector(".sidebar-wrapper") as HTMLDivElement;
|
||||
const display = window.getComputedStyle(sidebarEl).display;
|
||||
if (display === "none") {
|
||||
sidebarEl.style.display = "flex";
|
||||
} else {
|
||||
sidebarEl.style.display = "none";
|
||||
}
|
||||
};
|
||||
|
||||
export default Sidebar;
|
||||
|
@ -1,5 +1,7 @@
|
||||
import axios from "axios";
|
||||
|
||||
axios.defaults.withCredentials = true;
|
||||
|
||||
type ResponseObject<T> = {
|
||||
data: T;
|
||||
error?: string;
|
||||
|
@ -1,6 +1,4 @@
|
||||
@import "./mixin.less";
|
||||
|
||||
#root {
|
||||
.flex(row, flex-start, flex-start);
|
||||
@apply w-full h-full;
|
||||
}
|
||||
|
@ -1,31 +1,13 @@
|
||||
@import "./mixin.less";
|
||||
|
||||
* {
|
||||
@apply m-0 p-0 box-border;
|
||||
color: @text-black;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
body,
|
||||
html {
|
||||
@apply w-screen h-screen overflow-hidden text-base;
|
||||
@apply text-base;
|
||||
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",
|
||||
"Noto Color Emoji";
|
||||
}
|
||||
|
||||
code {
|
||||
@apply bg-pink-600 p-1 rounded font-mono;
|
||||
}
|
||||
|
||||
pre {
|
||||
@apply font-mono;
|
||||
|
||||
* {
|
||||
@apply font-mono;
|
||||
}
|
||||
}
|
||||
|
||||
label,
|
||||
button,
|
||||
img {
|
||||
@ -57,17 +39,10 @@ li {
|
||||
}
|
||||
|
||||
a {
|
||||
@apply cursor-pointer text-blue-600 underline underline-offset-2;
|
||||
|
||||
&:hover {
|
||||
@apply opacity-80;
|
||||
}
|
||||
@apply cursor-pointer text-blue-600 underline underline-offset-2 hover:opacity-80;
|
||||
}
|
||||
|
||||
.btn {
|
||||
@apply select-none cursor-pointer text-center;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
@ -1,15 +1,19 @@
|
||||
@import "./mixin.less";
|
||||
@import "./memos-header.less";
|
||||
|
||||
#root {
|
||||
.page-wrapper.home {
|
||||
@apply relative top-0 w-full h-screen overflow-y-auto;
|
||||
background-color: #f6f5f4;
|
||||
}
|
||||
|
||||
#page-wrapper {
|
||||
@apply w-full h-full m-auto grid max-w-4xl mx-auto;
|
||||
grid-template-columns: min-content 1fr;
|
||||
> .page-container {
|
||||
@apply relative w-full min-h-screen mx-auto flex flex-row justify-center items-start;
|
||||
|
||||
.memos-wrapper {
|
||||
@apply w-full h-full overflow-x-hidden flex flex-col justify-start items-start px-4 pr-10;
|
||||
>.sidebar-wrapper{
|
||||
@apply flex-shrink-0;
|
||||
}
|
||||
|
||||
> .memos-wrapper {
|
||||
@apply relative w-full max-w-2xl min-h-full overflow-x-hidden flex flex-col justify-start items-start px-4 sm:pr-6;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,10 @@
|
||||
@import "./mixin.less";
|
||||
|
||||
.dialog-wrapper.memo-card-dialog {
|
||||
> .dialog-container {
|
||||
@apply p-0 bg-transparent;
|
||||
@apply px-4;
|
||||
|
||||
> * {
|
||||
@apply shrink-0;
|
||||
}
|
||||
> .dialog-container {
|
||||
@apply w-full p-0 bg-transparent flex flex-col justify-start items-center;
|
||||
|
||||
> .memo-card-container {
|
||||
.flex(column, flex-start, flex-start);
|
||||
|
@ -1,7 +1,8 @@
|
||||
@import "./mixin.less";
|
||||
@import "./memos-header.less";
|
||||
|
||||
.memo-trash-dialog {
|
||||
@apply px-4;
|
||||
|
||||
> .dialog-container {
|
||||
@apply w-128 max-w-full mb-8;
|
||||
|
||||
|
@ -5,20 +5,22 @@
|
||||
.flex(row, space-between, center);
|
||||
@apply w-full h-10 flex-nowrap mt-4 mb-2 shrink-0;
|
||||
|
||||
> .title-text {
|
||||
.flex(row, flex-start, center);
|
||||
@apply font-bold text-lg leading-10 mr-2 text-ellipsis shrink-0 cursor-pointer overflow-hidden;
|
||||
color: @text-black;
|
||||
> .title-container {
|
||||
@apply flex flex-row justify-start items-center mr-2 shrink-0 overflow-hidden;
|
||||
|
||||
> .action-btn {
|
||||
.flex(row, center, center);
|
||||
@apply w-6 h-6 mr-1 shrink-0;
|
||||
@apply flex sm:hidden flex-row justify-center items-center w-6 h-6 mr-1 shrink-0;
|
||||
background-color: unset;
|
||||
|
||||
> .icon-img {
|
||||
@apply w-4 h-auto;
|
||||
@apply w-5 h-auto;
|
||||
}
|
||||
}
|
||||
|
||||
> .title-text {
|
||||
@apply font-bold text-lg leading-10 mr-2 text-ellipsis shrink-0 cursor-pointer overflow-hidden;
|
||||
color: @text-black;
|
||||
}
|
||||
}
|
||||
|
||||
> .btns-container {
|
||||
|
@ -2,22 +2,14 @@
|
||||
|
||||
.menu-btns-popup {
|
||||
.flex(column, flex-start, flex-start);
|
||||
@apply absolute mt-1 ml-24 p-1 w-44 rounded-lg z-10 shadow bg-white;
|
||||
|
||||
&:hover {
|
||||
display: flex;
|
||||
}
|
||||
@apply absolute right-0 top-0 mt-1 p-1 w-36 rounded-lg z-10 shadow bg-white;
|
||||
|
||||
> .btn {
|
||||
.flex(row, flex-start, center);
|
||||
@apply w-full py-2 px-3 text-base rounded text-left;
|
||||
@apply w-full py-2 px-3 text-base rounded text-left hover:bg-gray-100;
|
||||
|
||||
> .icon {
|
||||
@apply block w-6 text-center mr-2 text-base;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: @bg-whitegray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,10 +15,6 @@
|
||||
@bg-light-blue: #eef3fe;
|
||||
@bg-paper-yellow: #fbf4de;
|
||||
|
||||
.mono-font-family {
|
||||
font-family: SFMono-Regular, Menlo, Consolas, "PT Mono", "Liberation Mono", Courier, monospace;
|
||||
}
|
||||
|
||||
.hide-scroll-bar {
|
||||
.pretty-scroll-bar(0, 0);
|
||||
|
||||
|
@ -23,8 +23,7 @@
|
||||
}
|
||||
|
||||
> .quickly-action-wrapper {
|
||||
@apply hidden absolute top-9 -right-2 p-2 w-80;
|
||||
z-index: 2;
|
||||
@apply hidden absolute top-9 -right-2 p-2 w-80 z-10;
|
||||
|
||||
> .quickly-action-container {
|
||||
.flex(column, flex-start, flex-start);
|
||||
|
@ -1,13 +1,14 @@
|
||||
@import "./mixin.less";
|
||||
@import "./memos-header.less";
|
||||
|
||||
.setting-dialog {
|
||||
@apply px-4;
|
||||
|
||||
> .dialog-container {
|
||||
@apply w-176 max-w-full mb-8 p-0;
|
||||
|
||||
> .dialog-content-container {
|
||||
.flex(column, flex-start, flex-start);
|
||||
@apply relative w-full overflow-y-scroll p-0 flex flex-row justify-start items-start;
|
||||
@apply relative w-full overflow-y-scroll p-0 flex flex-col sm:flex-row justify-start items-start;
|
||||
.hide-scroll-bar();
|
||||
|
||||
> .close-btn {
|
||||
@ -20,23 +21,28 @@
|
||||
}
|
||||
|
||||
> .section-selector-container {
|
||||
@apply w-40 h-full shrink-0 rounded-l-lg p-4 border-r bg-gray-100 flex flex-col justify-start items-start;
|
||||
@apply w-full sm:w-40 h-auto sm:h-full shrink-0 rounded-t-lg sm:rounded-l-lg p-4 border-r bg-gray-100 flex flex-col justify-start items-start;
|
||||
|
||||
> .section-title {
|
||||
@apply text-sm mt-4 first:mt-3 mb-1 font-mono text-gray-400;
|
||||
}
|
||||
|
||||
> .section-item {
|
||||
@apply text-base left-6 mt-2 text-gray-700 cursor-pointer hover:opacity-80;
|
||||
>.section-items-container{
|
||||
@apply w-full h-auto flex flex-row sm:flex-col justify-start items-start;
|
||||
|
||||
&.selected {
|
||||
@apply font-bold hover:opacity-100;
|
||||
> .section-item {
|
||||
@apply text-base mr-2 sm:mr-0 mt-2 text-gray-700 cursor-pointer hover:opacity-80;
|
||||
|
||||
&.selected {
|
||||
@apply font-bold hover:opacity-100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
> .section-content-container {
|
||||
@apply w-auto p-4 px-6 grow flex flex-col justify-start items-start h-128 overflow-y-scroll;
|
||||
@apply w-full sm:w-auto p-4 px-6 grow flex flex-col justify-start items-start h-128 overflow-y-scroll;
|
||||
|
||||
> .section-container {
|
||||
.flex(column, flex-start, flex-start);
|
||||
@ -47,8 +53,7 @@
|
||||
}
|
||||
|
||||
> .form-label {
|
||||
.flex(row, flex-start, center);
|
||||
@apply w-full mb-2;
|
||||
@apply flex flex-row justify-start items-center w-full mb-2;
|
||||
|
||||
> .normal-text {
|
||||
@apply shrink-0 select-text;
|
||||
|
@ -9,12 +9,14 @@
|
||||
}
|
||||
|
||||
&.username-label {
|
||||
@apply w-full flex-wrap;
|
||||
|
||||
> input {
|
||||
@apply grow-0 shadow-inner w-auto px-2 py-1 text-base border rounded leading-6 bg-transparent focus:border-black;
|
||||
@apply grow-0 shadow-inner w-auto px-2 py-1 mr-2 text-base border rounded leading-6 bg-transparent focus:border-black;
|
||||
}
|
||||
|
||||
> .btns-container {
|
||||
@apply ml-2 shrink-0 flex flex-row justify-start items-center;
|
||||
@apply mr-2 shrink-0 flex flex-row justify-start items-center;
|
||||
|
||||
> .btn {
|
||||
@apply text-sm shadow px-4 py-1 leading-6 rounded border hover:opacity-80 bg-gray-50;
|
||||
|
@ -1,10 +1,21 @@
|
||||
@import "./mixin.less";
|
||||
|
||||
.sidebar-wrapper {
|
||||
.flex(column, flex-start, flex-start);
|
||||
@apply w-64 h-full py-4 pl-2 overflow-x-hidden overflow-y-auto;
|
||||
@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;
|
||||
.hide-scroll-bar();
|
||||
|
||||
> .close-container {
|
||||
@apply w-full pr-6 my-2 flex sm:hidden flex-row justify-end items-center;
|
||||
|
||||
> .action-btn {
|
||||
@apply p-1 bg-gray-100 rounded shadow;
|
||||
|
||||
> .icon-img {
|
||||
@apply w-4 h-auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .action-btns-container {
|
||||
@apply w-full px-2 my-2 flex flex-col justify-start items-start shrink-0;
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
.page-wrapper.signin {
|
||||
.flex(row, center, center);
|
||||
@apply w-full h-full bg-white;
|
||||
@apply w-full min-h-screen bg-white;
|
||||
|
||||
> .page-container {
|
||||
@apply w-80 max-w-full py-4 -mt-16;
|
||||
|
@ -32,9 +32,9 @@ function Home() {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<section className="page-wrapper home">
|
||||
{loadingState.isLoading ? null : (
|
||||
<section id="page-wrapper">
|
||||
<div className="page-container">
|
||||
<Sidebar />
|
||||
<main className="memos-wrapper">
|
||||
<MemosHeader />
|
||||
@ -42,9 +42,9 @@ function Home() {
|
||||
<MemoFilter />
|
||||
<MemoList />
|
||||
</main>
|
||||
</section>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -16,5 +16,5 @@
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": ["./src", "./public"]
|
||||
"include": ["./src"]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user