feat: responsive view (#75)

* chore: add license

* feat: mobile view
This commit is contained in:
STEVEN 2022-06-19 11:32:49 +08:00 committed by GitHub
parent b96d78ed19
commit cd7000da70
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 149 additions and 119 deletions

21
LICENSE Normal file
View 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.

View file

@ -33,6 +33,5 @@
"tailwindcss": "^3.0.18",
"typescript": "^4.3.2",
"vite": "^2.9.0"
},
"license": "MIT"
}
}

View file

@ -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>
);

View file

@ -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) => {

View file

@ -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>

View file

@ -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>

View file

@ -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;

View file

@ -1,5 +1,7 @@
import axios from "axios";
axios.defaults.withCredentials = true;
type ResponseObject<T> = {
data: T;
error?: string;

View file

@ -1,6 +1,4 @@
@import "./mixin.less";
#root {
.flex(row, flex-start, flex-start);
@apply w-full h-full;
}

View file

@ -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;
}

View file

@ -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;
}
}
}

View file

@ -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);

View file

@ -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;

View file

@ -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 {

View file

@ -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;
}
}
}

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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>
);
}

View file

@ -16,5 +16,5 @@
"noEmit": true,
"jsx": "react-jsx"
},
"include": ["./src", "./public"]
"include": ["./src"]
}